| /* |
| * 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 |