X86: Make the local APIC handle interrupt messages from the IO APIC.
authorGabe Black <gblack@eecs.umich.edu>
Sun, 12 Oct 2008 20:44:24 +0000 (13:44 -0700)
committerGabe Black <gblack@eecs.umich.edu>
Sun, 12 Oct 2008 20:44:24 +0000 (13:44 -0700)
src/arch/x86/faults.hh
src/arch/x86/interrupts.cc
src/arch/x86/interrupts.hh
src/arch/x86/intmessage.hh
src/dev/x86/i82094aa.cc
src/dev/x86/i8259.hh
src/dev/x86/pc.cc

index a54bc8ee9767f2fb0f9b14b9a21ecf897a715ad5..8fe90299d9212c40147a62719ace393c3e950ea7 100644 (file)
@@ -351,9 +351,10 @@ namespace X86ISA
 
     class ExternalInterrupt : public X86Interrupt
     {
+        uint8_t vector;
       public:
-        ExternalInterrupt() :
-            X86Interrupt("External Interrupt", "#INTR")
+        ExternalInterrupt(uint8_t _vector) :
+            X86Interrupt("External Interrupt", "#INTR"), vector(_vector)
         {}
     };
 
index 5614a37eb03633c95b958127c292d212816e09e8..a7c238acecb88460a84ed15ffdb9ec587b73ad9f 100644 (file)
@@ -57,7 +57,9 @@
 
 #include "arch/x86/apicregs.hh"
 #include "arch/x86/interrupts.hh"
+#include "arch/x86/intmessage.hh"
 #include "cpu/base.hh"
+#include "mem/packet_access.hh"
 
 int
 divideFromConf(uint32_t conf)
@@ -242,12 +244,43 @@ X86ISA::Interrupts::write(PacketPtr pkt)
 Tick
 X86ISA::Interrupts::recvMessage(PacketPtr pkt)
 {
-    Addr offset = pkt->getAddr() - x86InterruptAddress(0, 0);
+    uint8_t id = 0;
+    Addr offset = pkt->getAddr() - x86InterruptAddress(id, 0);
     assert(pkt->cmd == MemCmd::MessageReq);
     switch(offset)
     {
       case 0:
-        DPRINTF(LocalApic, "Got Trigger Interrupt message.\n");
+        {
+            TriggerIntMessage message = pkt->get<TriggerIntMessage>();
+            uint8_t vector = message.vector;
+            DPRINTF(LocalApic,
+                    "Got Trigger Interrupt message with vector %#x.\n",
+                    vector);
+            // Make sure we're really supposed to get this.
+            assert((message.destMode == 0 && message.destination == id) ||
+                   (bits((int)message.destination, id)));
+            if (DeliveryMode::isUnmaskable(message.deliveryMode)) {
+                DPRINTF(LocalApic, "Interrupt is an %s and unmaskable.\n",
+                        DeliveryMode::names[message.deliveryMode]);
+                panic("Unmaskable interrupts aren't implemented.\n");
+            } else if (DeliveryMode::isMaskable(message.deliveryMode)) {
+                DPRINTF(LocalApic, "Interrupt is an %s and maskable.\n",
+                        DeliveryMode::names[message.deliveryMode]);
+                // Queue up the interrupt in the IRR.
+                if (vector > IRRV)
+                    IRRV = vector;
+                if (!getRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, vector)) {
+                    setRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, vector);
+                    if (message.trigger) {
+                        // Level triggered.
+                        setRegArrayBit(APIC_TRIGGER_MODE_BASE, vector);
+                    } else {
+                        // Edge triggered.
+                        clearRegArrayBit(APIC_TRIGGER_MODE_BASE, vector);
+                    }
+                }
+            }
+        }
         break;
       default:
         panic("Local apic got unknown interrupt message at offset %#x.\n",
@@ -414,6 +447,36 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val)
     return;
 }
 
+bool
+X86ISA::Interrupts::check_interrupts(ThreadContext * tc) const
+{
+    RFLAGS rflags = tc->readMiscRegNoEffect(MISCREG_RFLAGS);
+    if (IRRV > ISRV && rflags.intf &&
+            bits(IRRV, 7, 4) > bits(regs[APIC_TASK_PRIORITY], 7, 4)) {
+        return true;
+    }
+    return false;
+}
+
+Fault
+X86ISA::Interrupts::getInterrupt(ThreadContext * tc)
+{
+    assert(check_interrupts(tc));
+    return new ExternalInterrupt(IRRV);
+}
+
+void
+X86ISA::Interrupts::updateIntrInfo(ThreadContext * tc)
+{
+    assert(check_interrupts(tc));
+    // Mark the interrupt as "in service".
+    ISRV = IRRV;
+    setRegArrayBit(APIC_IN_SERVICE_BASE, ISRV);
+    // Clear it out of the IRR.
+    clearRegArrayBit(APIC_INTERRUPT_REQUEST_BASE, IRRV);
+    updateIRRV();
+}
+
 X86ISA::Interrupts *
 X86LocalApicParams::create()
 {
index c4760dc0f4964c9b913c5d3ad68bf60c88dce734..cfc1ada9d9c994f636201598697866a29caf97f1 100644 (file)
@@ -60,6 +60,7 @@
 
 #include "arch/x86/apicregs.hh"
 #include "arch/x86/faults.hh"
+#include "base/bitfield.hh"
 #include "cpu/thread_context.hh"
 #include "dev/io_device.hh"
 #include "dev/x86/intdev.hh"
@@ -74,7 +75,12 @@ namespace X86ISA
 class Interrupts : public BasicPioDevice, IntDev
 {
   protected:
+    // Storage for the APIC registers
     uint32_t regs[NUM_APIC_REGS];
+
+    /*
+     * Timing related stuff.
+     */
     Tick latency;
     Tick clock;
 
@@ -92,7 +98,58 @@ class Interrupts : public BasicPioDevice, IntDev
 
     ApicTimerEvent apicTimerEvent;
 
+    /*
+     * IRR and ISR maintenance.
+     */
+    uint8_t IRRV;
+    uint8_t ISRV;
+
+    int
+    findRegArrayMSB(ApicRegIndex base)
+    {
+        int offset = 7;
+        do {
+            if (regs[base + offset] != 0) {
+                return offset * 32 + findMsbSet(regs[base + offset]);
+            }
+        } while (offset--);
+        return 0;
+    }
+
+    void
+    updateIRRV()
+    {
+        IRRV = findRegArrayMSB(APIC_INTERRUPT_REQUEST_BASE);
+    }
+
+    void
+    updateISRV()
+    {
+        ISRV = findRegArrayMSB(APIC_IN_SERVICE_BASE);
+    }
+
+    void
+    setRegArrayBit(ApicRegIndex base, uint8_t vector)
+    {
+        regs[base + (vector % 32)] |= (1 << (vector >> 5));
+    }
+
+    void
+    clearRegArrayBit(ApicRegIndex base, uint8_t vector)
+    {
+        regs[base + (vector % 32)] &= ~(1 << (vector >> 5));
+    }
+
+    bool
+    getRegArrayBit(ApicRegIndex base, uint8_t vector)
+    {
+        return bits(regs[base + (vector % 32)], vector >> 5);
+    }
+
   public:
+    /*
+     * Params stuff.
+     */
     typedef X86LocalApicParams Params;
 
     void setClock(Tick newClock)
@@ -106,6 +163,9 @@ class Interrupts : public BasicPioDevice, IntDev
         return dynamic_cast<const Params *>(_params);
     }
 
+    /*
+     * Functions to interact with the interrupt port from IntDev.
+     */
     Tick read(PacketPtr pkt);
     Tick write(PacketPtr pkt);
     Tick recvMessage(PacketPtr pkt);
@@ -124,6 +184,17 @@ class Interrupts : public BasicPioDevice, IntDev
                     x86InterruptAddress(0, 0) + PhysAddrAPICRangeSize));
     }
 
+    Port *getPort(const std::string &if_name, int idx = -1)
+    {
+        if (if_name == "int_port")
+            return intPort;
+        return BasicPioDevice::getPort(if_name, idx);
+    }
+
+    /*
+     * Functions to access and manipulate the APIC's registers.
+     */
+
     uint32_t readReg(ApicRegIndex miscReg);
     void setReg(ApicRegIndex reg, uint32_t val);
     void setRegNoEffect(ApicRegIndex reg, uint32_t val)
@@ -131,29 +202,47 @@ class Interrupts : public BasicPioDevice, IntDev
         regs[reg] = val;
     }
 
+    /*
+     * Constructor.
+     */
+
     Interrupts(Params * p) : BasicPioDevice(p), IntDev(this),
                              latency(p->pio_latency), clock(0)
     {
         pioSize = PageBytes;
+        memset(regs, 0, sizeof(regs));
         //Set the local apic DFR to the flat model.
         regs[APIC_DESTINATION_FORMAT] = (uint32_t)(-1);
-        memset(regs, 0, sizeof(regs));
-        clear_all();
+        ISRV = 0;
+        IRRV = 0;
     }
 
-    Port *getPort(const std::string &if_name, int idx = -1)
+    /*
+     * Functions for retrieving interrupts for the CPU to handle.
+     */
+
+    bool check_interrupts(ThreadContext * tc) const;
+    Fault getInterrupt(ThreadContext * tc);
+    void updateIntrInfo(ThreadContext * tc);
+
+    /*
+     * Serialization.
+     */
+
+    void serialize(std::ostream & os)
     {
-        if (if_name == "int_port")
-            return intPort;
-        return BasicPioDevice::getPort(if_name, idx);
+        panic("Interrupts::serialize unimplemented!\n");
     }
 
-    int InterruptLevel(uint64_t softint)
+    void unserialize(Checkpoint * cp, const std::string & section)
     {
-        panic("Interrupts::InterruptLevel unimplemented!\n");
-        return 0;
+        panic("Interrupts::unserialize unimplemented!\n");
     }
 
+    /*
+     * Old functions needed for compatability but which will be phased out
+     * eventually.
+     */
     void post(int int_num, int index)
     {
         panic("Interrupts::post unimplemented!\n");
@@ -161,37 +250,12 @@ class Interrupts : public BasicPioDevice, IntDev
 
     void clear(int int_num, int index)
     {
-        warn("Interrupts::clear unimplemented!\n");
+        panic("Interrupts::clear unimplemented!\n");
     }
 
     void clear_all()
     {
-        warn("Interrupts::clear_all unimplemented!\n");
-    }
-
-    bool check_interrupts(ThreadContext * tc) const
-    {
-        return false;
-    }
-
-    Fault getInterrupt(ThreadContext * tc)
-    {
-        return NoFault;
-    }
-
-    void updateIntrInfo(ThreadContext * tc)
-    {
-        panic("Interrupts::updateIntrInfo unimplemented!\n");
-    }
-
-    void serialize(std::ostream & os)
-    {
-        panic("Interrupts::serialize unimplemented!\n");
-    }
-
-    void unserialize(Checkpoint * cp, const std::string & section)
-    {
-        panic("Interrupts::unserialize unimplemented!\n");
+        panic("Interrupts::clear_all unimplemented!\n");
     }
 };
 
index 64e821a419fa230c16d000e0d77d71e8487e55dc..a018a997b1f2e00a43fc3881e25b2dbfa1771192 100644 (file)
@@ -44,8 +44,47 @@ namespace X86ISA
         Bitfield<15, 8> vector;
         Bitfield<18, 16> deliveryMode;
         Bitfield<19> destMode;
+        Bitfield<20> level;
+        Bitfield<21> trigger;
     EndBitUnion(TriggerIntMessage)
 
+    namespace DeliveryMode
+    {
+        enum IntDeliveryMode {
+            Fixed = 0,
+            LowestPriority = 1,
+            SMI = 2,
+            NMI = 4,
+            INIT = 5,
+            ExtInt = 7,
+            NumModes
+        };
+
+        static const char * const names[NumModes] = {
+            "Fixed", "LowestPriority", "SMI", "Reserved",
+            "NMI", "INIT", "Reserved", "ExtInt"
+        };
+
+        static inline bool
+        isUnmaskable(int mode)
+        {
+            return (mode == SMI || mode == NMI ||
+                    mode == INIT || mode == ExtInt);
+        }
+
+        static inline bool
+        isMaskable(int mode)
+        {
+            return (mode == Fixed || mode == LowestPriority);
+        }
+
+        static inline bool
+        isReserved(int mode)
+        {
+            return !(isMaskable(mode) || isUnmaskable(mode));
+        }
+    }
+
     static const Addr TriggerIntOffset = 0;
 
     static inline PacketPtr
index 1ae3b01cfd4125b132ddcb8580788646a42604b6..d7f479106f34593948123d4add11cfd4949ff7ac 100644 (file)
@@ -146,33 +146,12 @@ X86ISA::I82094AA::signalInterrupt(int line)
         return;
     } else {
         if (DTRACE(I82094AA)) {
-            switch(entry.deliveryMode) {
-              case 0:
-                DPRINTF(I82094AA, "Delivery mode is: Fixed.\n");
-                break;
-              case 1:
-                DPRINTF(I82094AA, "Delivery mode is: Lowest Priority.\n");
-                break;
-              case 2:
-                DPRINTF(I82094AA, "Delivery mode is: SMI.\n");
-                break;
-              case 3:
+            if (DeliveryMode::isReserved(entry.deliveryMode)) {
                 fatal("Tried to use reserved delivery mode "
                         "for IO APIC entry %d.\n", line);
-                break;
-              case 4:
-                DPRINTF(I82094AA, "Delivery mode is: NMI.\n");
-                break;
-              case 5:
-                DPRINTF(I82094AA, "Delivery mode is: INIT.\n");
-                break;
-              case 6:
-                fatal("Tried to use reserved delivery mode "
-                        "for IO APIC entry %d.\n", line);
-                break;
-              case 7:
-                DPRINTF(I82094AA, "Delivery mode is: ExtINT.\n");
-                break;
+            } else {
+                DPRINTF(I82094AA, "Delivery mode is: %s.\n",
+                        DeliveryMode::names[entry.deliveryMode]);
             }
             DPRINTF(I82094AA, "Vector is %#x.\n", entry.vector);
         }
@@ -182,6 +161,8 @@ X86ISA::I82094AA::signalInterrupt(int line)
         message.vector = entry.vector;
         message.deliveryMode = entry.deliveryMode;
         message.destMode = entry.destMode;
+        message.level = entry.polarity;
+        message.trigger = entry.trigger;
 
         if (entry.destMode == 0) {
             DPRINTF(I82094AA,
index 1c14e53971bee58ab4123b975de182788c2ab32a..7804fe3b73776c4f9d68822ff6a30f3866e3de50 100644 (file)
@@ -76,7 +76,8 @@ class I8259 : public BasicPioDevice, public IntDev
 
     I8259(Params * p) : BasicPioDevice(p), IntDev(this),
                         latency(p->pio_latency), output(p->output),
-                        mode(p->mode), readIRR(true), initControlWord(0)
+                        mode(p->mode), IRR(0), ISR(0), IMR(0),
+                        readIRR(true), initControlWord(0)
     {
         pioSize = 2;
     }
index 4cd81dd50f98f57927caf7405338322b3e81763f..94fb23e219831a64c40dfc9195b482b752670f3d 100644 (file)
@@ -36,6 +36,7 @@
 #include <string>
 #include <vector>
 
+#include "arch/x86/intmessage.hh"
 #include "arch/x86/x86_traits.hh"
 #include "cpu/intr_control.hh"
 #include "dev/terminal.hh"
@@ -78,7 +79,7 @@ Pc::init()
      */
     I82094AA & ioApic = *southBridge->ioApic;
     I82094AA::RedirTableEntry entry = 0;
-    entry.deliveryMode = 0x7;
+    entry.deliveryMode = DeliveryMode::ExtInt;
     entry.vector = 0x20;
     ioApic.writeReg(0x10, entry.bottomDW);
     ioApic.writeReg(0x11, entry.topDW);