X86: Make the local APIC accessible through the memory system directly, and make...
authorGabe Black <gblack@eecs.umich.edu>
Sun, 12 Oct 2008 18:08:00 +0000 (11:08 -0700)
committerGabe Black <gblack@eecs.umich.edu>
Sun, 12 Oct 2008 18:08:00 +0000 (11:08 -0700)
src/arch/x86/interrupts.cc
src/arch/x86/interrupts.hh
src/arch/x86/miscregfile.cc
src/arch/x86/miscregs.hh
src/arch/x86/mmaped_ipr.hh
src/arch/x86/tlb.cc
src/arch/x86/utility.cc
src/arch/x86/x86_traits.hh
src/cpu/BaseCPU.py

index 73536f2b4afe3a745084c851f167d470d542ce61..5814859b34bdd45e95c0a4c74217faf8d8b494a6 100644 (file)
  * 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);
-}
-
-void
-X86ISA::Interrupts::setRegNoEffect(ApicRegIndex reg, uint32_t val)
-{
-    regs[reg] = val;
+    return regs[reg];
 }
 
 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;
 }
 
index 68bb2e07d49e7bcbd3e9d5da7c7aca569bf4bc15..1d5f600bd81f9d70671c8f47bbeb6c54987f342b 100644 (file)
@@ -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<const Params *>(_params);
     }
 
-    uint32_t readRegNoEffect(ApicRegIndex reg);
-    uint32_t readReg(ApicRegIndex miscReg, ThreadContext *tc);
+    Tick read(PacketPtr pkt);
+    Tick write(PacketPtr pkt);
+
+    void addressRanges(AddrRangeList &range_list)
+    {
+        range_list.clear();
+        range_list.push_back(RangeEx(x86LocalAPICAddress(0, 0),
+                                     x86LocalAPICAddress(0, 0) + PageBytes));
+    }
 
-    void setRegNoEffect(ApicRegIndex reg, uint32_t val);
-    void setReg(ApicRegIndex reg, uint32_t val, ThreadContext *tc);
+    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) : SimObject(p)
+    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));
index aba49861628fdafaa7d2c28516a87f0a7970d667..388a83e8df9c89b044c211ade5983435cfce0f04 100644 (file)
@@ -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<Interrupts *>(
-                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<Interrupts *>(
-                tc->getCpuPtr()->getInterruptController());
-        assert(interrupts);
-        interrupts->setReg(
-                ApicRegIndex(miscReg - MISCREG_APIC_START), val, tc);
-        return;
-    }
-#endif
     switch(miscReg)
     {
       case MISCREG_CR0:
index dbf08ec5d52dd82fc3fb469921c4090861633b29..a536d9e3b1c36140651cfdc6208b4d1221c0567c 100644 (file)
@@ -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)
     {
index 67a0b239e37db2c3f44029edef8fe203fe1c4558..7056c09026d74d956eaf8d9061423f4cc7e1f219 100644 (file)
@@ -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);
index 616f026cf4ad9ed9821df640e31a3571cd054e0e..5db6789194085731e4049400e451116c7d4b52cd 100644 (file)
@@ -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;
index 852d72a2e9b16f8d7b9c7ad59c97829c8de1537d..43a5ca1a96c8863c0f2edbc2d4829e0b5606e2b0 100644 (file)
@@ -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
 
index f46279c81c004659918c6350d0c299dc0d3084cd..6b4671a0812bddc9487657c4a11ecf9c479bd9ea 100644 (file)
  * Authors: Gabe Black
  */
 
-#include "sim/host.hh"
-
 #ifndef __ARCH_X86_X86TRAITS_HH__
 #define __ARCH_X86_X86TRAITS_HH__
 
+#include <assert.h>
+
+#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__
index c2385f6d0bbe3ed920f3d2a0a3951b002a985ad2..78b9ae9442ebdaf7818fac5fce772d5c68247efa 100644 (file)
@@ -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: