X86: Make the local APIC handle interrupt messages from the IO APIC.
This commit is contained in:
parent
4d5c7f7038
commit
876f4845f2
7 changed files with 225 additions and 75 deletions
|
@ -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)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue