From 876f4845f258ed09d348135d8af8cf4a17de1b8a Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Sun, 12 Oct 2008 13:44:24 -0700 Subject: [PATCH] X86: Make the local APIC handle interrupt messages from the IO APIC. --- src/arch/x86/faults.hh | 5 +- src/arch/x86/interrupts.cc | 67 +++++++++++++++- src/arch/x86/interrupts.hh | 152 ++++++++++++++++++++++++++----------- src/arch/x86/intmessage.hh | 39 ++++++++++ src/dev/x86/i82094aa.cc | 31 ++------ src/dev/x86/i8259.hh | 3 +- src/dev/x86/pc.cc | 3 +- 7 files changed, 225 insertions(+), 75 deletions(-) diff --git a/src/arch/x86/faults.hh b/src/arch/x86/faults.hh index a54bc8ee9..8fe90299d 100644 --- a/src/arch/x86/faults.hh +++ b/src/arch/x86/faults.hh @@ -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) {} }; diff --git a/src/arch/x86/interrupts.cc b/src/arch/x86/interrupts.cc index 5614a37eb..a7c238ace 100644 --- a/src/arch/x86/interrupts.cc +++ b/src/arch/x86/interrupts.cc @@ -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(); + 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() { diff --git a/src/arch/x86/interrupts.hh b/src/arch/x86/interrupts.hh index c4760dc0f..cfc1ada9d 100644 --- a/src/arch/x86/interrupts.hh +++ b/src/arch/x86/interrupts.hh @@ -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(_params); } + /* + * Functions to interact with the interrupt port from IntDev. + */ Tick read(PacketPtr pkt); Tick write(PacketPtr pkt); Tick recvMessage(PacketPtr pkt); @@ -124,23 +184,6 @@ class Interrupts : public BasicPioDevice, IntDev x86InterruptAddress(0, 0) + PhysAddrAPICRangeSize)); } - 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), IntDev(this), - 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)); - clear_all(); - } - Port *getPort(const std::string &if_name, int idx = -1) { if (if_name == "int_port") @@ -148,41 +191,43 @@ class Interrupts : public BasicPioDevice, IntDev return BasicPioDevice::getPort(if_name, idx); } - int InterruptLevel(uint64_t softint) + /* + * 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) { - panic("Interrupts::InterruptLevel unimplemented!\n"); - return 0; + regs[reg] = val; } - void post(int int_num, int index) + /* + * Constructor. + */ + + Interrupts(Params * p) : BasicPioDevice(p), IntDev(this), + latency(p->pio_latency), clock(0) { - panic("Interrupts::post unimplemented!\n"); + pioSize = PageBytes; + memset(regs, 0, sizeof(regs)); + //Set the local apic DFR to the flat model. + regs[APIC_DESTINATION_FORMAT] = (uint32_t)(-1); + ISRV = 0; + IRRV = 0; } - void clear(int int_num, int index) - { - warn("Interrupts::clear unimplemented!\n"); - } + /* + * Functions for retrieving interrupts for the CPU to handle. + */ - void clear_all() - { - warn("Interrupts::clear_all unimplemented!\n"); - } + bool check_interrupts(ThreadContext * tc) const; + Fault getInterrupt(ThreadContext * tc); + void updateIntrInfo(ThreadContext * tc); - bool check_interrupts(ThreadContext * tc) const - { - return false; - } - - Fault getInterrupt(ThreadContext * tc) - { - return NoFault; - } - - void updateIntrInfo(ThreadContext * tc) - { - panic("Interrupts::updateIntrInfo unimplemented!\n"); - } + /* + * Serialization. + */ void serialize(std::ostream & os) { @@ -193,6 +238,25 @@ class Interrupts : public BasicPioDevice, IntDev { 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"); + } + + void clear(int int_num, int index) + { + panic("Interrupts::clear unimplemented!\n"); + } + + void clear_all() + { + panic("Interrupts::clear_all unimplemented!\n"); + } }; }; diff --git a/src/arch/x86/intmessage.hh b/src/arch/x86/intmessage.hh index 64e821a41..a018a997b 100644 --- a/src/arch/x86/intmessage.hh +++ b/src/arch/x86/intmessage.hh @@ -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 diff --git a/src/dev/x86/i82094aa.cc b/src/dev/x86/i82094aa.cc index 1ae3b01cf..d7f479106 100644 --- a/src/dev/x86/i82094aa.cc +++ b/src/dev/x86/i82094aa.cc @@ -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, diff --git a/src/dev/x86/i8259.hh b/src/dev/x86/i8259.hh index 1c14e5397..7804fe3b7 100644 --- a/src/dev/x86/i8259.hh +++ b/src/dev/x86/i8259.hh @@ -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; } diff --git a/src/dev/x86/pc.cc b/src/dev/x86/pc.cc index 4cd81dd50..94fb23e21 100644 --- a/src/dev/x86/pc.cc +++ b/src/dev/x86/pc.cc @@ -36,6 +36,7 @@ #include #include +#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);