| /* | |
| * Copyright (c) 2009 The Regents of The University of Michigan | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer; | |
| * redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution; | |
| * neither the name of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| #include "arch/x86/isa.hh" | |
| #include "arch/x86/decoder.hh" | |
| #include "arch/x86/mmu.hh" | |
| #include "arch/x86/regs/ccr.hh" | |
| #include "arch/x86/regs/float.hh" | |
| #include "arch/x86/regs/int.hh" | |
| #include "arch/x86/regs/misc.hh" | |
| #include "base/compiler.hh" | |
| #include "cpu/base.hh" | |
| #include "cpu/thread_context.hh" | |
| #include "params/X86ISA.hh" | |
| #include "sim/serialize.hh" | |
| namespace gem5 | |
| { | |
| namespace X86ISA | |
| { | |
| void | |
| ISA::updateHandyM5Reg(Efer efer, CR0 cr0, | |
| SegAttr csAttr, SegAttr ssAttr, RFLAGS rflags) | |
| { | |
| HandyM5Reg m5reg = 0; | |
| if (efer.lma) { | |
| m5reg.mode = LongMode; | |
| if (csAttr.longMode) | |
| m5reg.submode = SixtyFourBitMode; | |
| else | |
| m5reg.submode = CompatabilityMode; | |
| } else { | |
| m5reg.mode = LegacyMode; | |
| if (cr0.pe) { | |
| if (rflags.vm) | |
| m5reg.submode = Virtual8086Mode; | |
| else | |
| m5reg.submode = ProtectedMode; | |
| } else { | |
| m5reg.submode = RealMode; | |
| } | |
| } | |
| m5reg.cpl = csAttr.dpl; | |
| m5reg.paging = cr0.pg; | |
| m5reg.prot = cr0.pe; | |
| // Compute the default and alternate operand size. | |
| if (m5reg.submode == SixtyFourBitMode || csAttr.defaultSize) { | |
| m5reg.defOp = 2; | |
| m5reg.altOp = 1; | |
| } else { | |
| m5reg.defOp = 1; | |
| m5reg.altOp = 2; | |
| } | |
| // Compute the default and alternate address size. | |
| if (m5reg.submode == SixtyFourBitMode) { | |
| m5reg.defAddr = 3; | |
| m5reg.altAddr = 2; | |
| } else if (csAttr.defaultSize) { | |
| m5reg.defAddr = 2; | |
| m5reg.altAddr = 1; | |
| } else { | |
| m5reg.defAddr = 1; | |
| m5reg.altAddr = 2; | |
| } | |
| // Compute the stack size | |
| if (m5reg.submode == SixtyFourBitMode) { | |
| m5reg.stack = 3; | |
| } else if (ssAttr.defaultSize) { | |
| m5reg.stack = 2; | |
| } else { | |
| m5reg.stack = 1; | |
| } | |
| regVal[misc_reg::M5Reg] = m5reg; | |
| if (tc) | |
| tc->getDecoderPtr()->as<Decoder>().setM5Reg(m5reg); | |
| } | |
| void | |
| ISA::clear() | |
| { | |
| // Blank everything. 0 might not be an appropriate value for some things, | |
| // but it is for most. | |
| memset(regVal, 0, misc_reg::NumRegs * sizeof(RegVal)); | |
| // If some state should be non-zero after a reset, set those values here. | |
| regVal[misc_reg::Cr0] = 0x0000000060000010ULL; | |
| regVal[misc_reg::Mtrrcap] = 0x0508; | |
| regVal[misc_reg::McgCap] = 0x104; | |
| regVal[misc_reg::Pat] = 0x0007040600070406ULL; | |
| regVal[misc_reg::Syscfg] = 0x20601; | |
| regVal[misc_reg::TopMem] = 0x4000000; | |
| regVal[misc_reg::Dr6] = (mask(8) << 4) | (mask(16) << 16); | |
| regVal[misc_reg::Dr7] = 1 << 10; | |
| LocalApicBase lApicBase = 0; | |
| lApicBase.base = 0xFEE00000 >> 12; | |
| lApicBase.enable = 1; | |
| // The "bsp" bit will be set when this register is read, since then we'll | |
| // have a ThreadContext to check the contextId from. | |
| regVal[misc_reg::ApicBase] = lApicBase; | |
| } | |
| namespace | |
| { | |
| /* Not applicable to X86 */ | |
| RegClass vecRegClass(VecRegClass, VecRegClassName, 1, debug::IntRegs); | |
| RegClass vecElemClass(VecElemClass, VecElemClassName, 2, debug::IntRegs); | |
| RegClass vecPredRegClass(VecPredRegClass, VecPredRegClassName, 1, | |
| debug::IntRegs); | |
| } // anonymous namespace | |
| ISA::ISA(const X86ISAParams &p) : BaseISA(p), vendorString(p.vendor_string) | |
| { | |
| fatal_if(vendorString.size() != 12, | |
| "CPUID vendor string must be 12 characters\n"); | |
| _regClasses.push_back(&flatIntRegClass); | |
| _regClasses.push_back(&flatFloatRegClass); | |
| _regClasses.push_back(&vecRegClass); | |
| _regClasses.push_back(&vecElemClass); | |
| _regClasses.push_back(&vecPredRegClass); | |
| _regClasses.push_back(&ccRegClass); | |
| _regClasses.push_back(&miscRegClass); | |
| clear(); | |
| } | |
| static void | |
| copyMiscRegs(ThreadContext *src, ThreadContext *dest) | |
| { | |
| // This function assumes no side effects other than TLB invalidation | |
| // need to be considered while copying state. That will likely not be | |
| // true in the future. | |
| for (int i = 0; i < misc_reg::NumRegs; ++i) { | |
| if (!misc_reg::isValid(i)) | |
| continue; | |
| dest->setMiscRegNoEffect(i, src->readMiscRegNoEffect(i)); | |
| } | |
| // The TSC has to be updated with side-effects if the CPUs in a | |
| // CPU switch have different frequencies. | |
| dest->setMiscReg(misc_reg::Tsc, src->readMiscReg(misc_reg::Tsc)); | |
| dest->getMMUPtr()->flushAll(); | |
| } | |
| void | |
| ISA::copyRegsFrom(ThreadContext *src) | |
| { | |
| //copy int regs | |
| for (auto &id: flatIntRegClass) | |
| tc->setReg(id, src->getReg(id)); | |
| //copy float regs | |
| for (auto &id: flatFloatRegClass) | |
| tc->setReg(id, src->getReg(id)); | |
| //copy condition-code regs | |
| for (auto &id: ccRegClass) | |
| tc->setReg(id, src->getReg(id)); | |
| copyMiscRegs(src, tc); | |
| tc->pcState(src->pcState()); | |
| } | |
| RegVal | |
| ISA::readMiscRegNoEffect(RegIndex idx) const | |
| { | |
| // Make sure we're not dealing with an illegal control register. | |
| // Instructions should filter out these indexes, and nothing else should | |
| // attempt to read them directly. | |
| assert(misc_reg::isValid(idx)); | |
| return regVal[idx]; | |
| } | |
| RegVal | |
| ISA::readMiscReg(RegIndex idx) | |
| { | |
| if (idx == misc_reg::Tsc) { | |
| return regVal[misc_reg::Tsc] + tc->getCpuPtr()->curCycle(); | |
| } | |
| if (idx == misc_reg::Fsw) { | |
| RegVal fsw = regVal[misc_reg::Fsw]; | |
| RegVal top = regVal[misc_reg::X87Top]; | |
| return insertBits(fsw, 13, 11, top); | |
| } | |
| if (idx == misc_reg::ApicBase) { | |
| LocalApicBase base = regVal[misc_reg::ApicBase]; | |
| base.bsp = (tc->contextId() == 0); | |
| return base; | |
| } | |
| return readMiscRegNoEffect(idx); | |
| } | |
| void | |
| ISA::setMiscRegNoEffect(RegIndex idx, RegVal val) | |
| { | |
| // Make sure we're not dealing with an illegal control register. | |
| // Instructions should filter out these indexes, and nothing else should | |
| // attempt to write to them directly. | |
| assert(misc_reg::isValid(idx)); | |
| HandyM5Reg m5Reg = regVal[misc_reg::M5Reg]; | |
| int reg_width = 64; | |
| switch (idx) { | |
| case misc_reg::X87Top: | |
| reg_width = 3; | |
| break; | |
| case misc_reg::Ftw: | |
| reg_width = 8; | |
| break; | |
| case misc_reg::Fsw: | |
| case misc_reg::Fcw: | |
| case misc_reg::Fop: | |
| reg_width = 16; | |
| break; | |
| case misc_reg::Mxcsr: | |
| reg_width = 32; | |
| break; | |
| case misc_reg::Fiseg: | |
| case misc_reg::Foseg: | |
| if (m5Reg.submode != SixtyFourBitMode) | |
| reg_width = 16; | |
| break; | |
| case misc_reg::Fioff: | |
| case misc_reg::Fooff: | |
| if (m5Reg.submode != SixtyFourBitMode) | |
| reg_width = 32; | |
| break; | |
| default: | |
| break; | |
| } | |
| regVal[idx] = val & mask(reg_width); | |
| } | |
| void | |
| ISA::setMiscReg(RegIndex idx, RegVal val) | |
| { | |
| RegVal newVal = val; | |
| switch (idx) { | |
| case misc_reg::Cr0: | |
| { | |
| CR0 toggled = regVal[idx] ^ val; | |
| CR0 newCR0 = val; | |
| Efer efer = regVal[misc_reg::Efer]; | |
| if (toggled.pg && efer.lme) { | |
| if (newCR0.pg) { | |
| //Turning on long mode | |
| efer.lma = 1; | |
| regVal[misc_reg::Efer] = efer; | |
| } else { | |
| //Turning off long mode | |
| efer.lma = 0; | |
| regVal[misc_reg::Efer] = efer; | |
| } | |
| } | |
| if (toggled.pg) { | |
| tc->getMMUPtr()->flushAll(); | |
| } | |
| //This must always be 1. | |
| newCR0.et = 1; | |
| newVal = newCR0; | |
| updateHandyM5Reg(regVal[misc_reg::Efer], | |
| newCR0, | |
| regVal[misc_reg::CsAttr], | |
| regVal[misc_reg::SsAttr], | |
| regVal[misc_reg::Rflags]); | |
| } | |
| break; | |
| case misc_reg::Cr2: | |
| break; | |
| case misc_reg::Cr3: | |
| static_cast<MMU *>(tc->getMMUPtr())->flushNonGlobal(); | |
| break; | |
| case misc_reg::Cr4: | |
| { | |
| CR4 toggled = regVal[idx] ^ val; | |
| if (toggled.pae || toggled.pse || toggled.pge) { | |
| tc->getMMUPtr()->flushAll(); | |
| } | |
| } | |
| break; | |
| case misc_reg::Cr8: | |
| break; | |
| case misc_reg::Rflags: | |
| { | |
| RFLAGS rflags = val; | |
| panic_if(rflags.vm, "Virtual 8086 mode is not supported."); | |
| break; | |
| } | |
| case misc_reg::CsAttr: | |
| { | |
| SegAttr toggled = regVal[idx] ^ val; | |
| SegAttr newCSAttr = val; | |
| if (toggled.longMode) { | |
| if (newCSAttr.longMode) { | |
| regVal[misc_reg::EsEffBase] = 0; | |
| regVal[misc_reg::CsEffBase] = 0; | |
| regVal[misc_reg::SsEffBase] = 0; | |
| regVal[misc_reg::DsEffBase] = 0; | |
| } else { | |
| regVal[misc_reg::EsEffBase] = regVal[misc_reg::EsBase]; | |
| regVal[misc_reg::CsEffBase] = regVal[misc_reg::CsBase]; | |
| regVal[misc_reg::SsEffBase] = regVal[misc_reg::SsBase]; | |
| regVal[misc_reg::DsEffBase] = regVal[misc_reg::DsBase]; | |
| } | |
| } | |
| updateHandyM5Reg(regVal[misc_reg::Efer], | |
| regVal[misc_reg::Cr0], | |
| newCSAttr, | |
| regVal[misc_reg::SsAttr], | |
| regVal[misc_reg::Rflags]); | |
| } | |
| break; | |
| case misc_reg::SsAttr: | |
| updateHandyM5Reg(regVal[misc_reg::Efer], | |
| regVal[misc_reg::Cr0], | |
| regVal[misc_reg::CsAttr], | |
| val, | |
| regVal[misc_reg::Rflags]); | |
| break; | |
| // These segments always actually use their bases, or in other words | |
| // their effective bases must stay equal to their actual bases. | |
| case misc_reg::FsBase: | |
| case misc_reg::GsBase: | |
| case misc_reg::HsBase: | |
| case misc_reg::TslBase: | |
| case misc_reg::TsgBase: | |
| case misc_reg::TrBase: | |
| case misc_reg::IdtrBase: | |
| regVal[misc_reg::segEffBase(idx - misc_reg::SegBaseBase)] = val; | |
| break; | |
| // These segments ignore their bases in 64 bit mode. | |
| // their effective bases must stay equal to their actual bases. | |
| case misc_reg::EsBase: | |
| case misc_reg::CsBase: | |
| case misc_reg::SsBase: | |
| case misc_reg::DsBase: | |
| { | |
| Efer efer = regVal[misc_reg::Efer]; | |
| SegAttr csAttr = regVal[misc_reg::CsAttr]; | |
| if (!efer.lma || !csAttr.longMode) // Check for non 64 bit mode. | |
| regVal[misc_reg::segEffBase(idx - | |
| misc_reg::SegBaseBase)] = val; | |
| } | |
| break; | |
| case misc_reg::Tsc: | |
| regVal[misc_reg::Tsc] = val - tc->getCpuPtr()->curCycle(); | |
| return; | |
| case misc_reg::Dr0: | |
| case misc_reg::Dr1: | |
| case misc_reg::Dr2: | |
| case misc_reg::Dr3: | |
| /* These should eventually set up breakpoints. */ | |
| break; | |
| case misc_reg::Dr4: | |
| idx = misc_reg::Dr6; | |
| [[fallthrough]]; | |
| case misc_reg::Dr6: | |
| { | |
| DR6 dr6 = regVal[misc_reg::Dr6]; | |
| DR6 newDR6 = val; | |
| dr6.b0 = newDR6.b0; | |
| dr6.b1 = newDR6.b1; | |
| dr6.b2 = newDR6.b2; | |
| dr6.b3 = newDR6.b3; | |
| dr6.bd = newDR6.bd; | |
| dr6.bs = newDR6.bs; | |
| dr6.bt = newDR6.bt; | |
| newVal = dr6; | |
| } | |
| break; | |
| case misc_reg::Dr5: | |
| idx = misc_reg::Dr7; | |
| [[fallthrough]]; | |
| case misc_reg::Dr7: | |
| { | |
| DR7 dr7 = regVal[misc_reg::Dr7]; | |
| DR7 newDR7 = val; | |
| dr7.l0 = newDR7.l0; | |
| dr7.g0 = newDR7.g0; | |
| if (dr7.l0 || dr7.g0) { | |
| panic("Debug register breakpoints not implemented.\n"); | |
| } else { | |
| /* Disable breakpoint 0. */ | |
| } | |
| dr7.l1 = newDR7.l1; | |
| dr7.g1 = newDR7.g1; | |
| if (dr7.l1 || dr7.g1) { | |
| panic("Debug register breakpoints not implemented.\n"); | |
| } else { | |
| /* Disable breakpoint 1. */ | |
| } | |
| dr7.l2 = newDR7.l2; | |
| dr7.g2 = newDR7.g2; | |
| if (dr7.l2 || dr7.g2) { | |
| panic("Debug register breakpoints not implemented.\n"); | |
| } else { | |
| /* Disable breakpoint 2. */ | |
| } | |
| dr7.l3 = newDR7.l3; | |
| dr7.g3 = newDR7.g3; | |
| if (dr7.l3 || dr7.g3) { | |
| panic("Debug register breakpoints not implemented.\n"); | |
| } else { | |
| /* Disable breakpoint 3. */ | |
| } | |
| dr7.gd = newDR7.gd; | |
| dr7.rw0 = newDR7.rw0; | |
| dr7.len0 = newDR7.len0; | |
| dr7.rw1 = newDR7.rw1; | |
| dr7.len1 = newDR7.len1; | |
| dr7.rw2 = newDR7.rw2; | |
| dr7.len2 = newDR7.len2; | |
| dr7.rw3 = newDR7.rw3; | |
| dr7.len3 = newDR7.len3; | |
| } | |
| break; | |
| case misc_reg::M5Reg: | |
| // Writing anything to the m5reg with side effects makes it update | |
| // based on the current values of the relevant registers. The actual | |
| // value written is discarded. | |
| updateHandyM5Reg(regVal[misc_reg::Efer], | |
| regVal[misc_reg::Cr0], | |
| regVal[misc_reg::CsAttr], | |
| regVal[misc_reg::SsAttr], | |
| regVal[misc_reg::Rflags]); | |
| return; | |
| default: | |
| break; | |
| } | |
| setMiscRegNoEffect(idx, newVal); | |
| } | |
| void | |
| ISA::serialize(CheckpointOut &cp) const | |
| { | |
| SERIALIZE_ARRAY(regVal, misc_reg::NumRegs); | |
| } | |
| void | |
| ISA::unserialize(CheckpointIn &cp) | |
| { | |
| UNSERIALIZE_ARRAY(regVal, misc_reg::NumRegs); | |
| updateHandyM5Reg(regVal[misc_reg::Efer], | |
| regVal[misc_reg::Cr0], | |
| regVal[misc_reg::CsAttr], | |
| regVal[misc_reg::SsAttr], | |
| regVal[misc_reg::Rflags]); | |
| } | |
| void | |
| ISA::setThreadContext(ThreadContext *_tc) | |
| { | |
| BaseISA::setThreadContext(_tc); | |
| tc->getDecoderPtr()->as<Decoder>().setM5Reg(regVal[misc_reg::M5Reg]); | |
| } | |
| std::string | |
| ISA::getVendorString() const | |
| { | |
| return vendorString; | |
| } | |
| } // namespace X86ISA | |
| } // namespace gem5 |