X86: Make APICs communicate through the memory system.
authorGabe Black <gblack@eecs.umich.edu>
Sun, 12 Oct 2008 20:28:54 +0000 (13:28 -0700)
committerGabe Black <gblack@eecs.umich.edu>
Sun, 12 Oct 2008 20:28:54 +0000 (13:28 -0700)
12 files changed:
src/arch/x86/X86LocalApic.py
src/arch/x86/interrupts.cc
src/arch/x86/interrupts.hh
src/arch/x86/intmessage.hh [new file with mode: 0644]
src/arch/x86/x86_traits.hh
src/cpu/BaseCPU.py
src/dev/x86/I82094AA.py
src/dev/x86/SouthBridge.py
src/dev/x86/i82094aa.cc
src/dev/x86/i82094aa.hh
src/dev/x86/i8259.hh
src/dev/x86/intdev.hh

index 94e32ae5050332157dc118cc793b1e3113d817f7..483c65ef888c81604a09fcd748dc99e904462c7b 100644 (file)
@@ -33,3 +33,4 @@ class X86LocalApic(BasicPioDevice):
     type = 'X86LocalApic'
     cxx_class = 'X86ISA::Interrupts'
     pio_latency = Param.Latency('1ns', 'Programmed IO latency in simticks')
+    int_port = Port("Port for sending and receiving interrupt messages")
index 6f1920de0193c777fb587c379f29bb73beecab72..5614a37eb03633c95b958127c292d212816e09e8 100644 (file)
@@ -239,6 +239,27 @@ X86ISA::Interrupts::write(PacketPtr pkt)
     return latency;
 }
 
+Tick
+X86ISA::Interrupts::recvMessage(PacketPtr pkt)
+{
+    Addr offset = pkt->getAddr() - x86InterruptAddress(0, 0);
+    assert(pkt->cmd == MemCmd::MessageReq);
+    switch(offset)
+    {
+      case 0:
+        DPRINTF(LocalApic, "Got Trigger Interrupt message.\n");
+        break;
+      default:
+        panic("Local apic got unknown interrupt message at offset %#x.\n",
+                offset);
+        break;
+    }
+    delete pkt->req;
+    delete pkt;
+    return latency;
+}
+
+
 uint32_t
 X86ISA::Interrupts::readReg(ApicRegIndex reg)
 {
index 1d5f600bd81f9d70671c8f47bbeb6c54987f342b..c4760dc0f4964c9b913c5d3ad68bf60c88dce734 100644 (file)
 #include "arch/x86/faults.hh"
 #include "cpu/thread_context.hh"
 #include "dev/io_device.hh"
+#include "dev/x86/intdev.hh"
 #include "params/X86LocalApic.hh"
 #include "sim/eventq.hh"
-#include "sim/sim_object.hh"
 
 class ThreadContext;
 
 namespace X86ISA
 {
 
-class Interrupts : public BasicPioDevice
+class Interrupts : public BasicPioDevice, IntDev
 {
   protected:
     uint32_t regs[NUM_APIC_REGS];
@@ -108,6 +108,7 @@ class Interrupts : public BasicPioDevice
 
     Tick read(PacketPtr pkt);
     Tick write(PacketPtr pkt);
+    Tick recvMessage(PacketPtr pkt);
 
     void addressRanges(AddrRangeList &range_list)
     {
@@ -116,6 +117,13 @@ class Interrupts : public BasicPioDevice
                                      x86LocalAPICAddress(0, 0) + PageBytes));
     }
 
+    void getIntAddrRange(AddrRangeList &range_list)
+    {
+        range_list.clear();
+        range_list.push_back(RangeEx(x86InterruptAddress(0, 0),
+                    x86InterruptAddress(0, 0) + PhysAddrAPICRangeSize));
+    }
+
     uint32_t readReg(ApicRegIndex miscReg);
     void setReg(ApicRegIndex reg, uint32_t val);
     void setRegNoEffect(ApicRegIndex reg, uint32_t val)
@@ -123,7 +131,7 @@ class Interrupts : public BasicPioDevice
         regs[reg] = val;
     }
 
-    Interrupts(Params * p) : BasicPioDevice(p),
+    Interrupts(Params * p) : BasicPioDevice(p), IntDev(this),
                              latency(p->pio_latency), clock(0)
     {
         pioSize = PageBytes;
@@ -133,6 +141,13 @@ class Interrupts : public BasicPioDevice
         clear_all();
     }
 
+    Port *getPort(const std::string &if_name, int idx = -1)
+    {
+        if (if_name == "int_port")
+            return intPort;
+        return BasicPioDevice::getPort(if_name, idx);
+    }
+
     int InterruptLevel(uint64_t softint)
     {
         panic("Interrupts::InterruptLevel unimplemented!\n");
diff --git a/src/arch/x86/intmessage.hh b/src/arch/x86/intmessage.hh
new file mode 100644 (file)
index 0000000..64e821a
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2008 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.
+ *
+ * Authors: Gabe Black
+ */
+
+#ifndef __ARCH_X86_INTMESSAGE_HH__
+#define __ARCH_X86_INTMESSAGE_HH__
+
+#include "arch/x86/x86_traits.hh"
+#include "base/bitunion.hh"
+#include "mem/packet.hh"
+#include "mem/request.hh"
+#include "sim/host.hh"
+
+namespace X86ISA
+{
+    BitUnion32(TriggerIntMessage)
+        Bitfield<7, 0> destination;
+        Bitfield<15, 8> vector;
+        Bitfield<18, 16> deliveryMode;
+        Bitfield<19> destMode;
+    EndBitUnion(TriggerIntMessage)
+
+    static const Addr TriggerIntOffset = 0;
+
+    static inline PacketPtr
+    prepIntRequest(const uint8_t id, Addr offset, Addr size)
+    {
+        RequestPtr req = new Request(x86InterruptAddress(id, offset),
+                                     size, UNCACHEABLE);
+        PacketPtr pkt = new Packet(req, MemCmd::MessageReq, Packet::Broadcast);
+        pkt->allocate();
+        return pkt;
+    }
+
+    template<class T>
+    PacketPtr
+    buildIntRequest(const uint8_t id, T payload, Addr offset, Addr size)
+    {
+        PacketPtr pkt = prepIntRequest(id, offset, size);
+        pkt->set<T>(payload);
+        return pkt;
+    }
+
+    static inline PacketPtr
+    buildIntRequest(const uint8_t id, TriggerIntMessage payload)
+    {
+        return buildIntRequest(id, payload, TriggerIntOffset,
+                sizeof(TriggerIntMessage));
+    }
+
+    static inline PacketPtr
+    buildIntResponse()
+    {
+        panic("buildIntResponse not implemented.\n");
+    }
+}
+
+#endif
index 6b4671a0812bddc9487657c4a11ecf9c479bd9ea..be7572517ef2cc2489486cb913a6fbccd0b1093e 100644 (file)
@@ -93,6 +93,7 @@ namespace X86ISA
     const Addr PhysAddrPrefixIO = ULL(0x8000000000000000);
     const Addr PhysAddrPrefixPciConfig = ULL(0xC000000000000000);
     const Addr PhysAddrPrefixLocalAPIC = ULL(0xA000000000000000);
+    const Addr PhysAddrPrefixInterrupts = ULL(0x2000000000000000);
     // 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;
@@ -115,6 +116,13 @@ namespace X86ISA
         assert(addr < (1 << 12));
         return PhysAddrPrefixLocalAPIC | (id * (1 << 12)) | addr;
     }
+
+    static inline Addr
+    x86InterruptAddress(const uint8_t id, const uint16_t addr)
+    {
+        assert(addr < PhysAddrAPICRangeSize);
+        return PhysAddrPrefixInterrupts | (id * PhysAddrAPICRangeSize) | addr;
+    }
 }
 
 #endif //__ARCH_X86_X86TRAITS_HH__
index 78b9ae9442ebdaf7818fac5fce772d5c68247efa..51d447f0b1fc3d1cee92668e5541a264920cdcce 100644 (file)
@@ -144,7 +144,8 @@ class BaseCPU(MemObject):
     if build_env['TARGET_ISA'] == 'x86' and build_env['FULL_SYSTEM']:
         _mem_ports = ["itb.walker.port",
                       "dtb.walker.port",
-                      "interrupts.pio"]
+                      "interrupts.pio",
+                      "interrupts.int_port"]
 
     def connectMemPorts(self, bus):
         for p in self._mem_ports:
index 731d831f0f35376db15c052bfe3ee2ad6090e3a0..b4ad96a9f4d5ec437a0302e67ec8034ee47bb4ba 100644 (file)
@@ -36,6 +36,7 @@ class I82094AA(BasicPioDevice):
     cxx_class = 'X86ISA::I82094AA'
     pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks")
     pio_addr = Param.Addr("Device address")
+    int_port = Port("Port for sending and receiving interrupt messages")
 
     def pin(self, line):
         return X86IntPin(device=self, line=line)
index 15ffe153f452a07b4971807230fe4fd36467a189..8a9bea01bc96bf4e445ccb3c87eeec515c1e0064 100644 (file)
@@ -72,3 +72,4 @@ class SouthBridge(SimObject):
         self.pit.pio = bus.port
         self.speaker.pio = bus.port
         self.io_apic.pio = bus.port
+        self.io_apic.int_port = bus.port
index 713f59592b18a0301557cc1abe706bfd126eac0a..f944e71715da488d8a38c0bb8fe2a518f76ffebf 100644 (file)
  * Authors: Gabe Black
  */
 
+#include "arch/x86/intmessage.hh"
 #include "dev/x86/i82094aa.hh"
 #include "mem/packet.hh"
 #include "mem/packet_access.hh"
 #include "sim/system.hh"
 
-X86ISA::I82094AA::I82094AA(Params *p) : PioDevice(p),
+X86ISA::I82094AA::I82094AA(Params *p) : PioDevice(p), IntDev(this),
    latency(p->pio_latency), pioAddr(p->pio_addr)
 {
     // This assumes there's only one I/O APIC in the system
@@ -140,11 +141,56 @@ X86ISA::I82094AA::signalInterrupt(int line)
         DPRINTF(I82094AA, "Entry was masked.\n");
         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:
+                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;
+            }
+            DPRINTF(I82094AA, "Vector is %#x.\n", entry.vector);
+        }
+
+        TriggerIntMessage message;
+        message.destination = entry.dest;
+        message.vector = entry.vector;
+        message.deliveryMode = entry.deliveryMode;
+        message.destMode = entry.destMode;
+
         if (entry.destMode == 0) {
             DPRINTF(I82094AA,
-                    "Would send interrupt to APIC ID %d.\n", entry.dest);
+                    "Sending interrupt to APIC ID %d.\n", entry.dest);
+            PacketPtr pkt = buildIntRequest(entry.dest, message);
+            if (sys->getMemoryMode() == Enums::timing)
+                intPort->sendMessageTiming(pkt, latency);
+            else if (sys->getMemoryMode() == Enums::atomic)
+                intPort->sendMessageAtomic(pkt);
+            else
+                panic("Unrecognized memory mode.\n");
         } else {
-            DPRINTF(I82094AA, "Would send interrupts to APIC IDs:"
+            DPRINTF(I82094AA, "Sending interrupts to APIC IDs:"
                     "%s%s%s%s%s%s%s%s\n",
                     bits((int)entry.dest, 0) ? " 0": "",
                     bits((int)entry.dest, 1) ? " 1": "",
@@ -155,36 +201,22 @@ X86ISA::I82094AA::signalInterrupt(int line)
                     bits((int)entry.dest, 6) ? " 6": "",
                     bits((int)entry.dest, 7) ? " 7": ""
                     );
+            uint8_t dests = entry.dest;
+            uint8_t id = 0;
+            while(dests) {
+                if (dests & 0x1) {
+                    PacketPtr pkt = buildIntRequest(id, message);
+                    if (sys->getMemoryMode() == Enums::timing)
+                        intPort->sendMessageTiming(pkt, latency);
+                    else if (sys->getMemoryMode() == Enums::atomic)
+                        intPort->sendMessageAtomic(pkt);
+                    else
+                        panic("Unrecognized memory mode.\n");
+                }
+                dests >>= 1;
+                id++;
+            }
         }
-        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:
-            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;
-        }
-        DPRINTF(I82094AA, "Vector is %#x.\n", entry.vector);
     }
 }
 
index b442e3e924b3e6f0d01dc91d3f38bd55ff0f98a9..6c874a5f96b90bc1dde5ff0223583345c213c010 100644 (file)
@@ -60,6 +60,7 @@ class I82094AA : public PioDevice, public IntDev
     EndBitUnion(RedirTableEntry)
 
   protected:
+    System * system;
     Tick latency;
     Addr pioAddr;
 
@@ -95,9 +96,23 @@ class I82094AA : public PioDevice, public IntDev
         range_list.push_back(RangeEx(pioAddr + 16, pioAddr + 20));
     }
 
+    void getIntAddrRange(AddrRangeList &range_list)
+    {
+        range_list.clear();
+        range_list.push_back(RangeEx(x86InterruptAddress(1, 0),
+                    x86InterruptAddress(1, 0) + PhysAddrAPICRangeSize));
+    }
+
     void writeReg(uint8_t offset, uint32_t value);
     uint32_t readReg(uint8_t offset);
 
+    Port *getPort(const std::string &if_name, int idx = -1)
+    {
+        if (if_name == "int_port")
+            return intPort;
+        return PioDevice::getPort(if_name, idx);
+    }
+
     void signalInterrupt(int line);
 };
 
index f3864422210e1683485475d7c1425a97b9a257c4..1c14e53971bee58ab4123b975de182788c2ab32a 100644 (file)
@@ -74,9 +74,9 @@ class I8259 : public BasicPioDevice, public IntDev
         return dynamic_cast<const Params *>(_params);
     }
 
-    I8259(Params * p) : BasicPioDevice(p), latency(p->pio_latency),
-                        output(p->output), mode(p->mode), readIRR(true),
-                        initControlWord(0)
+    I8259(Params * p) : BasicPioDevice(p), IntDev(this),
+                        latency(p->pio_latency), output(p->output),
+                        mode(p->mode), readIRR(true), initControlWord(0)
     {
         pioSize = 2;
     }
index 7a369f8fa3b7d922fe51459c14dbbd75b37a7b84..d36ed462a816bb17e1788dea8eae7789f5559455 100644 (file)
 #define __DEV_X86_INTDEV_HH__
 
 #include <assert.h>
+#include <string>
 
+#include "arch/x86/x86_traits.hh"
+#include "mem/mem_object.hh"
+#include "mem/mport.hh"
 #include "sim/sim_object.hh"
 #include "params/X86IntPin.hh"
 
@@ -40,11 +44,73 @@ namespace X86ISA {
 
 class IntDev
 {
+  protected:
+    class IntPort : public MessagePort
+    {
+        IntDev * device;
+        Tick latency;
+        Addr intAddr;
+      public:
+        IntPort(const std::string &_name, MemObject * _parent,
+                IntDev *dev, Tick _latency) :
+            MessagePort(_name, _parent), device(dev), latency(_latency)
+        {
+        }
+
+        void getDeviceAddressRanges(AddrRangeList &resp, bool &snoop)
+        {
+            snoop = false;
+            device->getIntAddrRange(resp);
+        }
+
+        Tick recvMessage(PacketPtr pkt)
+        {
+            return device->recvMessage(pkt);
+        }
+
+        void recvStatusChange(Status status)
+        {
+            if (status == RangeChange) {
+                sendStatusChange(Port::RangeChange);
+            }
+        }
+
+    };
+
+    IntPort * intPort;
+
   public:
+    IntDev(MemObject * parent, Tick latency = 0)
+    {
+        if (parent != NULL) {
+            intPort = new IntPort(parent->name() + ".int_port",
+                    parent, this, latency);
+        } else {
+            intPort = NULL;
+        }
+    }
+
     virtual ~IntDev()
     {}
+
+    virtual void
+    signalInterrupt(int line)
+    {
+        panic("signalInterrupt not implemented.\n");
+    }
+
+    virtual Tick
+    recvMessage(PacketPtr pkt)
+    {
+        panic("recvMessage not implemented.\n");
+        return 0;
+    }
+
     virtual void
-    signalInterrupt(int line) = 0;
+    getIntAddrRange(AddrRangeList &range_list)
+    {
+        panic("intAddrRange not implemented.\n");
+    }
 };
 
 class IntPin : public SimObject