From 42ebebf99a7d6ce2358b152f643b52c7946f9202 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Sun, 12 Oct 2008 11:08:00 -0700 Subject: [PATCH] X86: Make the local APIC accessible through the memory system directly, and make the timer work. --- src/arch/x86/interrupts.cc | 207 ++++++++++++++++++++++++++++++++---- src/arch/x86/interrupts.hh | 35 ++++-- src/arch/x86/miscregfile.cc | 25 ----- src/arch/x86/miscregs.hh | 11 -- src/arch/x86/mmaped_ipr.hh | 6 +- src/arch/x86/tlb.cc | 141 +----------------------- src/arch/x86/utility.cc | 2 + src/arch/x86/x86_traits.hh | 17 ++- src/cpu/BaseCPU.py | 9 +- 9 files changed, 244 insertions(+), 209 deletions(-) diff --git a/src/arch/x86/interrupts.cc b/src/arch/x86/interrupts.cc index 73536f2b4..5814859b3 100644 --- a/src/arch/x86/interrupts.cc +++ b/src/arch/x86/interrupts.cc @@ -55,10 +55,12 @@ * Authors: Gabe Black */ +#include "arch/x86/apicregs.hh" #include "arch/x86/interrupts.hh" #include "cpu/base.hh" -int divideFromConf(uint32_t conf) +int +divideFromConf(uint32_t conf) { // This figures out what division we want from the division configuration // register in the local APIC. The encoding is a little odd but it can @@ -68,14 +70,171 @@ int divideFromConf(uint32_t conf) return 1 << shift; } -uint32_t -X86ISA::Interrupts::readRegNoEffect(ApicRegIndex reg) +namespace X86ISA { - return regs[reg]; + +ApicRegIndex +decodeAddr(Addr paddr) +{ + ApicRegIndex regNum; + paddr &= ~mask(3); + switch (paddr) + { + case 0x20: + regNum = APIC_ID; + break; + case 0x30: + regNum = APIC_VERSION; + break; + case 0x80: + regNum = APIC_TASK_PRIORITY; + break; + case 0x90: + regNum = APIC_ARBITRATION_PRIORITY; + break; + case 0xA0: + regNum = APIC_PROCESSOR_PRIORITY; + break; + case 0xB0: + regNum = APIC_EOI; + break; + case 0xD0: + regNum = APIC_LOGICAL_DESTINATION; + break; + case 0xE0: + regNum = APIC_DESTINATION_FORMAT; + break; + case 0xF0: + regNum = APIC_SPURIOUS_INTERRUPT_VECTOR; + break; + case 0x100: + case 0x108: + case 0x110: + case 0x118: + case 0x120: + case 0x128: + case 0x130: + case 0x138: + case 0x140: + case 0x148: + case 0x150: + case 0x158: + case 0x160: + case 0x168: + case 0x170: + case 0x178: + regNum = APIC_IN_SERVICE((paddr - 0x100) / 0x8); + break; + case 0x180: + case 0x188: + case 0x190: + case 0x198: + case 0x1A0: + case 0x1A8: + case 0x1B0: + case 0x1B8: + case 0x1C0: + case 0x1C8: + case 0x1D0: + case 0x1D8: + case 0x1E0: + case 0x1E8: + case 0x1F0: + case 0x1F8: + regNum = APIC_TRIGGER_MODE((paddr - 0x180) / 0x8); + break; + case 0x200: + case 0x208: + case 0x210: + case 0x218: + case 0x220: + case 0x228: + case 0x230: + case 0x238: + case 0x240: + case 0x248: + case 0x250: + case 0x258: + case 0x260: + case 0x268: + case 0x270: + case 0x278: + regNum = APIC_INTERRUPT_REQUEST((paddr - 0x200) / 0x8); + break; + case 0x280: + regNum = APIC_ERROR_STATUS; + break; + case 0x300: + regNum = APIC_INTERRUPT_COMMAND_LOW; + break; + case 0x310: + regNum = APIC_INTERRUPT_COMMAND_HIGH; + break; + case 0x320: + regNum = APIC_LVT_TIMER; + break; + case 0x330: + regNum = APIC_LVT_THERMAL_SENSOR; + break; + case 0x340: + regNum = APIC_LVT_PERFORMANCE_MONITORING_COUNTERS; + break; + case 0x350: + regNum = APIC_LVT_LINT0; + break; + case 0x360: + regNum = APIC_LVT_LINT1; + break; + case 0x370: + regNum = APIC_LVT_ERROR; + break; + case 0x380: + regNum = APIC_INITIAL_COUNT; + break; + case 0x390: + regNum = APIC_CURRENT_COUNT; + break; + case 0x3E0: + regNum = APIC_DIVIDE_CONFIGURATION; + break; + default: + // A reserved register field. + panic("Accessed reserved register field %#x.\n", paddr); + break; + } + return regNum; +} +} + +Tick +X86ISA::Interrupts::read(PacketPtr pkt) +{ + Addr offset = pkt->getAddr() - pioAddr; + //Make sure we're at least only accessing one register. + if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3))) + panic("Accessed more than one register at a time in the APIC!\n"); + ApicRegIndex reg = decodeAddr(offset); + uint32_t val = htog(readReg(reg)); + pkt->setData(((uint8_t *)&val) + (offset & mask(3))); + return latency; +} + +Tick +X86ISA::Interrupts::write(PacketPtr pkt) +{ + Addr offset = pkt->getAddr() - pioAddr; + //Make sure we're at least only accessing one register. + if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3))) + panic("Accessed more than one register at a time in the APIC!\n"); + ApicRegIndex reg = decodeAddr(offset); + uint32_t val = regs[reg]; + pkt->writeData(((uint8_t *)&val) + (offset & mask(3))); + setReg(reg, gtoh(val)); + return latency; } uint32_t -X86ISA::Interrupts::readReg(ApicRegIndex reg, ThreadContext * tc) +X86ISA::Interrupts::readReg(ApicRegIndex reg) { if (reg >= APIC_TRIGGER_MODE(0) && reg <= APIC_TRIGGER_MODE(15)) { @@ -104,24 +263,19 @@ X86ISA::Interrupts::readReg(ApicRegIndex reg, ThreadContext * tc) break; case APIC_CURRENT_COUNT: { - uint32_t val = regs[reg] - tc->getCpuPtr()->curCycle(); + assert(clock); + uint32_t val = regs[reg] - curTick / clock; val /= (16 * divideFromConf(regs[APIC_DIVIDE_CONFIGURATION])); return val; } default: break; } - return readRegNoEffect(reg); + return regs[reg]; } void -X86ISA::Interrupts::setRegNoEffect(ApicRegIndex reg, uint32_t val) -{ - regs[reg] = val; -} - -void -X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc) +X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val) { uint32_t newVal = val; if (reg >= APIC_IN_SERVICE(0) && @@ -201,11 +355,24 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc) } break; case APIC_INITIAL_COUNT: - newVal = bits(val, 31, 0); - regs[APIC_CURRENT_COUNT] = - tc->getCpuPtr()->curCycle() + - (16 * divideFromConf(regs[APIC_DIVIDE_CONFIGURATION])) * newVal; - //FIXME This should schedule the timer event. + { + assert(clock); + newVal = bits(val, 31, 0); + uint32_t newCount = newVal * + (divideFromConf(regs[APIC_DIVIDE_CONFIGURATION]) * 16); + regs[APIC_CURRENT_COUNT] = newCount + curTick / clock; + // Find out how long a "tick" of the timer should take. + Tick timerTick = 16 * clock; + // Schedule on the edge of the next tick plus the new count. + Tick offset = curTick % timerTick; + if (offset) { + reschedule(apicTimerEvent, + curTick + (newCount + 1) * timerTick - offset, true); + } else { + reschedule(apicTimerEvent, + curTick + newCount * timerTick, true); + } + } break; case APIC_CURRENT_COUNT: //Local APIC Current Count register is read only. @@ -216,7 +383,7 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc) default: break; } - setRegNoEffect(reg, newVal); + regs[reg] = newVal; return; } diff --git a/src/arch/x86/interrupts.hh b/src/arch/x86/interrupts.hh index 68bb2e07d..1d5f600bd 100644 --- a/src/arch/x86/interrupts.hh +++ b/src/arch/x86/interrupts.hh @@ -61,6 +61,7 @@ #include "arch/x86/apicregs.hh" #include "arch/x86/faults.hh" #include "cpu/thread_context.hh" +#include "dev/io_device.hh" #include "params/X86LocalApic.hh" #include "sim/eventq.hh" #include "sim/sim_object.hh" @@ -70,10 +71,12 @@ class ThreadContext; namespace X86ISA { -class Interrupts : public SimObject +class Interrupts : public BasicPioDevice { protected: uint32_t regs[NUM_APIC_REGS]; + Tick latency; + Tick clock; class ApicTimerEvent : public Event { @@ -92,20 +95,38 @@ class Interrupts : public SimObject public: typedef X86LocalApicParams Params; + void setClock(Tick newClock) + { + clock = newClock; + } + const Params * params() const { return dynamic_cast(_params); } - uint32_t readRegNoEffect(ApicRegIndex reg); - uint32_t readReg(ApicRegIndex miscReg, ThreadContext *tc); + Tick read(PacketPtr pkt); + Tick write(PacketPtr pkt); - void setRegNoEffect(ApicRegIndex reg, uint32_t val); - void setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc); - - Interrupts(Params * p) : SimObject(p) + void addressRanges(AddrRangeList &range_list) { + range_list.clear(); + range_list.push_back(RangeEx(x86LocalAPICAddress(0, 0), + x86LocalAPICAddress(0, 0) + PageBytes)); + } + + uint32_t readReg(ApicRegIndex miscReg); + void setReg(ApicRegIndex reg, uint32_t val); + void setRegNoEffect(ApicRegIndex reg, uint32_t val) + { + regs[reg] = val; + } + + Interrupts(Params * p) : BasicPioDevice(p), + latency(p->pio_latency), clock(0) + { + pioSize = PageBytes; //Set the local apic DFR to the flat model. regs[APIC_DESTINATION_FORMAT] = (uint32_t)(-1); memset(regs, 0, sizeof(regs)); diff --git a/src/arch/x86/miscregfile.cc b/src/arch/x86/miscregfile.cc index aba498616..388a83e8d 100644 --- a/src/arch/x86/miscregfile.cc +++ b/src/arch/x86/miscregfile.cc @@ -119,23 +119,11 @@ MiscReg MiscRegFile::readRegNoEffect(MiscRegIndex miscReg) !(miscReg > MISCREG_CR8 && miscReg <= MISCREG_CR15)); - if (isApicReg(miscReg)) { - panic("Can't readRegNoEffect from the local APIC.\n"); - } return regVal[miscReg]; } MiscReg MiscRegFile::readReg(MiscRegIndex miscReg, ThreadContext * tc) { -#if FULL_SYSTEM - if (isApicReg(miscReg)) { - Interrupts * interrupts = dynamic_cast( - tc->getCpuPtr()->getInterruptController()); - assert(interrupts); - return interrupts->readReg( - (ApicRegIndex)(miscReg - MISCREG_APIC_START), tc); - } -#endif if (miscReg == MISCREG_TSC) { return regVal[MISCREG_TSC] + tc->getCpuPtr()->curCycle(); } @@ -152,9 +140,6 @@ void MiscRegFile::setRegNoEffect(MiscRegIndex miscReg, const MiscReg &val) miscReg < MISCREG_CR8) && !(miscReg > MISCREG_CR8 && miscReg <= MISCREG_CR15)); - if (isApicReg(miscReg)) { - panic("Can't setRegNoEffect from the local APIC.\n"); - } regVal[miscReg] = val; } @@ -162,16 +147,6 @@ void MiscRegFile::setReg(MiscRegIndex miscReg, const MiscReg &val, ThreadContext * tc) { MiscReg newVal = val; -#if FULL_SYSTEM - if (isApicReg(miscReg)) { - Interrupts * interrupts = dynamic_cast( - tc->getCpuPtr()->getInterruptController()); - assert(interrupts); - interrupts->setReg( - ApicRegIndex(miscReg - MISCREG_APIC_START), val, tc); - return; - } -#endif switch(miscReg) { case MISCREG_CR0: diff --git a/src/arch/x86/miscregs.hh b/src/arch/x86/miscregs.hh index dbf08ec5d..a536d9e3b 100644 --- a/src/arch/x86/miscregs.hh +++ b/src/arch/x86/miscregs.hh @@ -58,7 +58,6 @@ #ifndef __ARCH_X86_MISCREGS_HH__ #define __ARCH_X86_MISCREGS_HH__ -#include "arch/x86/apicregs.hh" #include "arch/x86/segmentregs.hh" #include "arch/x86/x86_traits.hh" #include "base/bitunion.hh" @@ -369,22 +368,12 @@ namespace X86ISA MISCREG_APIC_BASE, - // Space for the APIC registers - MISCREG_APIC_START, - MISCREG_APIC_END = MISCREG_APIC_START + NUM_APIC_REGS - 1, - // "Fake" MSRs for internally implemented devices MISCREG_PCI_CONFIG_ADDRESS, NUM_MISCREGS }; - static inline bool - isApicReg(MiscRegIndex index) - { - return index >= MISCREG_APIC_START && index <= MISCREG_APIC_END; - } - static inline MiscRegIndex MISCREG_CR(int index) { diff --git a/src/arch/x86/mmaped_ipr.hh b/src/arch/x86/mmaped_ipr.hh index 67a0b239e..7056c0902 100644 --- a/src/arch/x86/mmaped_ipr.hh +++ b/src/arch/x86/mmaped_ipr.hh @@ -97,11 +97,7 @@ namespace X86ISA Addr offset = pkt->getAddr() & mask(3); MiscRegIndex index = (MiscRegIndex)(pkt->getAddr() / sizeof(MiscReg)); MiscReg data; - if (isApicReg(index)) { - data = htog(xc->readMiscReg(index)); - } else { - data = htog(xc->readMiscRegNoEffect(index)); - } + data = htog(xc->readMiscRegNoEffect(index)); // Make sure we don't trot off the end of data. assert(offset + pkt->getSize() <= sizeof(MiscReg)); pkt->writeData(((uint8_t *)&data) + offset); diff --git a/src/arch/x86/tlb.cc b/src/arch/x86/tlb.cc index 616f026cf..5db678919 100644 --- a/src/arch/x86/tlb.cc +++ b/src/arch/x86/tlb.cc @@ -638,10 +638,9 @@ TLB::translate(RequestPtr &req, ThreadContext *tc, bool write, bool execute) // Check for an access to the local APIC #if FULL_SYSTEM LocalApicBase localApicBase = tc->readMiscRegNoEffect(MISCREG_APIC_BASE); - Addr baseAddr = localApicBase.base << 12; + Addr baseAddr = localApicBase.base * PageBytes; Addr paddr = req->getPaddr(); - if (baseAddr <= paddr && baseAddr + (1 << 12) > paddr) { - req->setMmapedIpr(true); + if (baseAddr <= paddr && baseAddr + PageBytes > paddr) { // The Intel developer's manuals say the below restrictions apply, // but the linux kernel, because of a compiler optimization, breaks // them. @@ -653,139 +652,9 @@ TLB::translate(RequestPtr &req, ThreadContext *tc, bool write, bool execute) if (req->getSize() != (32/8)) return new GeneralProtection(0); */ - - //Make sure we're at least only accessing one register. - if ((paddr & ~mask(3)) != ((paddr + req->getSize()) & ~mask(3))) - panic("Accessed more than one register at a time in the APIC!\n"); - MiscReg regNum; - Addr offset = paddr & mask(3); - paddr &= ~mask(3); - switch (paddr - baseAddr) - { - case 0x20: - regNum = APIC_ID; - break; - case 0x30: - regNum = APIC_VERSION; - break; - case 0x80: - regNum = APIC_TASK_PRIORITY; - break; - case 0x90: - regNum = APIC_ARBITRATION_PRIORITY; - break; - case 0xA0: - regNum = APIC_PROCESSOR_PRIORITY; - break; - case 0xB0: - regNum = APIC_EOI; - break; - case 0xD0: - regNum = APIC_LOGICAL_DESTINATION; - break; - case 0xE0: - regNum = APIC_DESTINATION_FORMAT; - break; - case 0xF0: - regNum = APIC_SPURIOUS_INTERRUPT_VECTOR; - break; - case 0x100: - case 0x108: - case 0x110: - case 0x118: - case 0x120: - case 0x128: - case 0x130: - case 0x138: - case 0x140: - case 0x148: - case 0x150: - case 0x158: - case 0x160: - case 0x168: - case 0x170: - case 0x178: - regNum = APIC_IN_SERVICE((paddr - baseAddr - 0x100) / 0x8); - break; - case 0x180: - case 0x188: - case 0x190: - case 0x198: - case 0x1A0: - case 0x1A8: - case 0x1B0: - case 0x1B8: - case 0x1C0: - case 0x1C8: - case 0x1D0: - case 0x1D8: - case 0x1E0: - case 0x1E8: - case 0x1F0: - case 0x1F8: - regNum = APIC_TRIGGER_MODE((paddr - baseAddr - 0x180) / 0x8); - break; - case 0x200: - case 0x208: - case 0x210: - case 0x218: - case 0x220: - case 0x228: - case 0x230: - case 0x238: - case 0x240: - case 0x248: - case 0x250: - case 0x258: - case 0x260: - case 0x268: - case 0x270: - case 0x278: - regNum = APIC_INTERRUPT_REQUEST((paddr - baseAddr - 0x200) / 0x8); - break; - case 0x280: - regNum = APIC_ERROR_STATUS; - break; - case 0x300: - regNum = APIC_INTERRUPT_COMMAND_LOW; - break; - case 0x310: - regNum = APIC_INTERRUPT_COMMAND_HIGH; - break; - case 0x320: - regNum = APIC_LVT_TIMER; - break; - case 0x330: - regNum = APIC_LVT_THERMAL_SENSOR; - break; - case 0x340: - regNum = APIC_LVT_PERFORMANCE_MONITORING_COUNTERS; - break; - case 0x350: - regNum = APIC_LVT_LINT0; - break; - case 0x360: - regNum = APIC_LVT_LINT1; - break; - case 0x370: - regNum = APIC_LVT_ERROR; - break; - case 0x380: - regNum = APIC_INITIAL_COUNT; - break; - case 0x390: - regNum = APIC_CURRENT_COUNT; - break; - case 0x3E0: - regNum = APIC_DIVIDE_CONFIGURATION; - break; - default: - // A reserved register field. - return new GeneralProtection(0); - break; - } - regNum += MISCREG_APIC_START; - req->setPaddr(regNum * sizeof(MiscReg) + offset); + // Force the access to be uncacheable. + req->setFlags(req->getFlags() | UNCACHEABLE); + req->setPaddr(x86LocalAPICAddress(tc->readCpuId(), paddr - baseAddr)); } #endif return NoFault; diff --git a/src/arch/x86/utility.cc b/src/arch/x86/utility.cc index 852d72a2e..43a5ca1a9 100644 --- a/src/arch/x86/utility.cc +++ b/src/arch/x86/utility.cc @@ -267,6 +267,8 @@ void initCPU(ThreadContext *tc, int cpuId) interrupts->setRegNoEffect(APIC_ID, cpuId << 24); interrupts->setRegNoEffect(APIC_VERSION, (5 << 16) | 0x14); + + interrupts->setClock(tc->getCpuPtr()->ticks(16)); // TODO Set the SMRAM base address (SMBASE) to 0x00030000 diff --git a/src/arch/x86/x86_traits.hh b/src/arch/x86/x86_traits.hh index f46279c81..6b4671a08 100644 --- a/src/arch/x86/x86_traits.hh +++ b/src/arch/x86/x86_traits.hh @@ -55,11 +55,13 @@ * Authors: Gabe Black */ -#include "sim/host.hh" - #ifndef __ARCH_X86_X86TRAITS_HH__ #define __ARCH_X86_X86TRAITS_HH__ +#include + +#include "sim/host.hh" + namespace X86ISA { const int NumMicroIntRegs = 16; @@ -90,6 +92,10 @@ namespace X86ISA const Addr PhysAddrPrefixIO = ULL(0x8000000000000000); const Addr PhysAddrPrefixPciConfig = ULL(0xC000000000000000); + const Addr PhysAddrPrefixLocalAPIC = ULL(0xA000000000000000); + // Each APIC gets two pages. One page is used for local apics to field + // accesses from the CPU, and the other is for all APICs to communicate. + const Addr PhysAddrAPICRangeSize = 1 << 12; static inline Addr x86IOAddress(const uint32_t port) @@ -102,6 +108,13 @@ namespace X86ISA { return PhysAddrPrefixPciConfig | addr; } + + static inline Addr + x86LocalAPICAddress(const uint8_t id, const uint16_t addr) + { + assert(addr < (1 << 12)); + return PhysAddrPrefixLocalAPIC | (id * (1 << 12)) | addr; + } } #endif //__ARCH_X86_X86TRAITS_HH__ diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py index c2385f6d0..78b9ae944 100644 --- a/src/cpu/BaseCPU.py +++ b/src/cpu/BaseCPU.py @@ -97,8 +97,9 @@ class BaseCPU(MemObject): dtb = Param.X86DTB(X86DTB(), "Data TLB") itb = Param.X86ITB(X86ITB(), "Instruction TLB") if build_env['FULL_SYSTEM']: - interrupts = Param.X86LocalApic( - X86LocalApic(), "Interrupt Controller") + _localApic = X86LocalApic(pio_addr=0xa000000000000000) + interrupts = \ + Param.X86LocalApic(_localApic, "Interrupt Controller") elif build_env['TARGET_ISA'] == 'mips': UnifiedTLB = Param.Bool(True, "Is this a Unified TLB?") dtb = Param.MipsDTB(MipsDTB(), "Data TLB") @@ -141,7 +142,9 @@ class BaseCPU(MemObject): _mem_ports = [] if build_env['TARGET_ISA'] == 'x86' and build_env['FULL_SYSTEM']: - _mem_ports = ["itb.walker.port", "dtb.walker.port"] + _mem_ports = ["itb.walker.port", + "dtb.walker.port", + "interrupts.pio"] def connectMemPorts(self, bus): for p in self._mem_ports: