X86: Make the local APIC handle interrupt messages from the IO APIC.

This commit is contained in:
Gabe Black 2008-10-12 13:44:24 -07:00
parent 4d5c7f7038
commit 876f4845f2
7 changed files with 225 additions and 75 deletions

View file

@ -351,9 +351,10 @@ namespace X86ISA
class ExternalInterrupt : public X86Interrupt class ExternalInterrupt : public X86Interrupt
{ {
uint8_t vector;
public: public:
ExternalInterrupt() : ExternalInterrupt(uint8_t _vector) :
X86Interrupt("External Interrupt", "#INTR") X86Interrupt("External Interrupt", "#INTR"), vector(_vector)
{} {}
}; };

View file

@ -57,7 +57,9 @@
#include "arch/x86/apicregs.hh" #include "arch/x86/apicregs.hh"
#include "arch/x86/interrupts.hh" #include "arch/x86/interrupts.hh"
#include "arch/x86/intmessage.hh"
#include "cpu/base.hh" #include "cpu/base.hh"
#include "mem/packet_access.hh"
int int
divideFromConf(uint32_t conf) divideFromConf(uint32_t conf)
@ -242,12 +244,43 @@ X86ISA::Interrupts::write(PacketPtr pkt)
Tick Tick
X86ISA::Interrupts::recvMessage(PacketPtr pkt) 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); assert(pkt->cmd == MemCmd::MessageReq);
switch(offset) switch(offset)
{ {
case 0: 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; break;
default: default:
panic("Local apic got unknown interrupt message at offset %#x.\n", panic("Local apic got unknown interrupt message at offset %#x.\n",
@ -414,6 +447,36 @@ X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val)
return; 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 * X86ISA::Interrupts *
X86LocalApicParams::create() X86LocalApicParams::create()
{ {

View file

@ -60,6 +60,7 @@
#include "arch/x86/apicregs.hh" #include "arch/x86/apicregs.hh"
#include "arch/x86/faults.hh" #include "arch/x86/faults.hh"
#include "base/bitfield.hh"
#include "cpu/thread_context.hh" #include "cpu/thread_context.hh"
#include "dev/io_device.hh" #include "dev/io_device.hh"
#include "dev/x86/intdev.hh" #include "dev/x86/intdev.hh"
@ -74,7 +75,12 @@ namespace X86ISA
class Interrupts : public BasicPioDevice, IntDev class Interrupts : public BasicPioDevice, IntDev
{ {
protected: protected:
// Storage for the APIC registers
uint32_t regs[NUM_APIC_REGS]; uint32_t regs[NUM_APIC_REGS];
/*
* Timing related stuff.
*/
Tick latency; Tick latency;
Tick clock; Tick clock;
@ -92,7 +98,58 @@ class Interrupts : public BasicPioDevice, IntDev
ApicTimerEvent apicTimerEvent; 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: public:
/*
* Params stuff.
*/
typedef X86LocalApicParams Params; typedef X86LocalApicParams Params;
void setClock(Tick newClock) void setClock(Tick newClock)
@ -106,6 +163,9 @@ class Interrupts : public BasicPioDevice, IntDev
return dynamic_cast<const Params *>(_params); return dynamic_cast<const Params *>(_params);
} }
/*
* Functions to interact with the interrupt port from IntDev.
*/
Tick read(PacketPtr pkt); Tick read(PacketPtr pkt);
Tick write(PacketPtr pkt); Tick write(PacketPtr pkt);
Tick recvMessage(PacketPtr pkt); Tick recvMessage(PacketPtr pkt);
@ -124,23 +184,6 @@ class Interrupts : public BasicPioDevice, IntDev
x86InterruptAddress(0, 0) + PhysAddrAPICRangeSize)); 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) Port *getPort(const std::string &if_name, int idx = -1)
{ {
if (if_name == "int_port") if (if_name == "int_port")
@ -148,41 +191,43 @@ class Interrupts : public BasicPioDevice, IntDev
return BasicPioDevice::getPort(if_name, idx); 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"); regs[reg] = val;
return 0;
} }
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) /*
{ * Functions for retrieving interrupts for the CPU to handle.
warn("Interrupts::clear unimplemented!\n"); */
}
void clear_all() bool check_interrupts(ThreadContext * tc) const;
{ Fault getInterrupt(ThreadContext * tc);
warn("Interrupts::clear_all unimplemented!\n"); void updateIntrInfo(ThreadContext * tc);
}
bool check_interrupts(ThreadContext * tc) const /*
{ * Serialization.
return false; */
}
Fault getInterrupt(ThreadContext * tc)
{
return NoFault;
}
void updateIntrInfo(ThreadContext * tc)
{
panic("Interrupts::updateIntrInfo unimplemented!\n");
}
void serialize(std::ostream & os) void serialize(std::ostream & os)
{ {
@ -193,6 +238,25 @@ class Interrupts : public BasicPioDevice, IntDev
{ {
panic("Interrupts::unserialize unimplemented!\n"); 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");
}
}; };
}; };

View file

@ -44,8 +44,47 @@ namespace X86ISA
Bitfield<15, 8> vector; Bitfield<15, 8> vector;
Bitfield<18, 16> deliveryMode; Bitfield<18, 16> deliveryMode;
Bitfield<19> destMode; Bitfield<19> destMode;
Bitfield<20> level;
Bitfield<21> trigger;
EndBitUnion(TriggerIntMessage) 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 const Addr TriggerIntOffset = 0;
static inline PacketPtr static inline PacketPtr

View file

@ -146,33 +146,12 @@ X86ISA::I82094AA::signalInterrupt(int line)
return; return;
} else { } else {
if (DTRACE(I82094AA)) { if (DTRACE(I82094AA)) {
switch(entry.deliveryMode) { if (DeliveryMode::isReserved(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 " fatal("Tried to use reserved delivery mode "
"for IO APIC entry %d.\n", line); "for IO APIC entry %d.\n", line);
break; } else {
case 4: DPRINTF(I82094AA, "Delivery mode is: %s.\n",
DPRINTF(I82094AA, "Delivery mode is: NMI.\n"); DeliveryMode::names[entry.deliveryMode]);
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); DPRINTF(I82094AA, "Vector is %#x.\n", entry.vector);
} }
@ -182,6 +161,8 @@ X86ISA::I82094AA::signalInterrupt(int line)
message.vector = entry.vector; message.vector = entry.vector;
message.deliveryMode = entry.deliveryMode; message.deliveryMode = entry.deliveryMode;
message.destMode = entry.destMode; message.destMode = entry.destMode;
message.level = entry.polarity;
message.trigger = entry.trigger;
if (entry.destMode == 0) { if (entry.destMode == 0) {
DPRINTF(I82094AA, DPRINTF(I82094AA,

View file

@ -76,7 +76,8 @@ class I8259 : public BasicPioDevice, public IntDev
I8259(Params * p) : BasicPioDevice(p), IntDev(this), I8259(Params * p) : BasicPioDevice(p), IntDev(this),
latency(p->pio_latency), output(p->output), 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; pioSize = 2;
} }

View file

@ -36,6 +36,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "arch/x86/intmessage.hh"
#include "arch/x86/x86_traits.hh" #include "arch/x86/x86_traits.hh"
#include "cpu/intr_control.hh" #include "cpu/intr_control.hh"
#include "dev/terminal.hh" #include "dev/terminal.hh"
@ -78,7 +79,7 @@ Pc::init()
*/ */
I82094AA & ioApic = *southBridge->ioApic; I82094AA & ioApic = *southBridge->ioApic;
I82094AA::RedirTableEntry entry = 0; I82094AA::RedirTableEntry entry = 0;
entry.deliveryMode = 0x7; entry.deliveryMode = DeliveryMode::ExtInt;
entry.vector = 0x20; entry.vector = 0x20;
ioApic.writeReg(0x10, entry.bottomDW); ioApic.writeReg(0x10, entry.bottomDW);
ioApic.writeReg(0x11, entry.topDW); ioApic.writeReg(0x11, entry.topDW);