c04d7cd10a
fixed some stuff in apic code: - interprocessor nmi's - lowest priority - fixed ppr - can write task_priority - scan priorities from high to low
473 lines
16 KiB
Plaintext
473 lines
16 KiB
Plaintext
----------------------------------------------------------------------
|
|
Patch name: patch.apic-mrieker
|
|
Author: mrieker
|
|
Date: 30 May 2002
|
|
|
|
Detailed description:
|
|
This a patch found on sf bug list from apparently mrieker :
|
|
|
|
fixed some stuff in apic code:
|
|
- interprocessor nmi's
|
|
- lowest priority
|
|
- fixed ppr
|
|
- can write task_priority
|
|
- scan priorities from high to low
|
|
|
|
Patch was created with:
|
|
cvs diff -u
|
|
Apply patch to what version:
|
|
cvs checked out on 30 May 2002
|
|
Instructions:
|
|
To patch, go to main bochs directory.
|
|
Type "patch -p0 < THIS_PATCH_FILE".
|
|
----------------------------------------------------------------------
|
|
Index: cpu/cpu.cc
|
|
===================================================================
|
|
RCS file: /cvsroot/bochs/bochs/cpu/cpu.cc,v
|
|
retrieving revision 1.28
|
|
diff -u -r1.28 cpu.cc
|
|
--- cpu/cpu.cc 18 Apr 2002 00:22:19 -0000 1.28
|
|
+++ cpu/cpu.cc 30 May 2002 11:58:24 -0000
|
|
@@ -144,7 +144,7 @@
|
|
// first instruction of int/trap handlers.
|
|
BX_CPU_THIS_PTR prev_eip = EIP; // commit new EIP
|
|
BX_CPU_THIS_PTR prev_esp = ESP; // commit new ESP
|
|
-
|
|
+
|
|
// Now we can handle things which are synchronous to instruction
|
|
// execution.
|
|
if (BX_CPU_THIS_PTR eflags.rf) {
|
|
@@ -446,7 +446,7 @@
|
|
#else
|
|
while (1) {
|
|
#endif
|
|
- if (BX_CPU_THIS_PTR INTR && BX_CPU_THIS_PTR eflags.if_) {
|
|
+ if ((BX_CPU_THIS_PTR INTR && BX_CPU_THIS_PTR eflags.if_) || BX_CPU_THIS_PTR nmi_queued) {
|
|
break;
|
|
}
|
|
BX_TICK1();
|
|
@@ -456,7 +456,7 @@
|
|
// must give the others a chance to simulate. If an interrupt has
|
|
// arrived, then clear the HALT condition; otherwise just return from
|
|
// the CPU loop with stop_reason STOP_CPU_HALTED.
|
|
- if (BX_CPU_THIS_PTR INTR && BX_CPU_THIS_PTR eflags.if_) {
|
|
+ if ((BX_CPU_THIS_PTR INTR && BX_CPU_THIS_PTR eflags.if_) || BX_CPU_THIS_PTR nmi_queued) {
|
|
// interrupt ends the HALT condition
|
|
BX_CPU_THIS_PTR debug_trap = 0; // clear traps for after resume
|
|
BX_CPU_THIS_PTR inhibit_mask = 0; // clear inhibits for after resume
|
|
@@ -510,6 +510,7 @@
|
|
// Priority 5: External Interrupts
|
|
// NMI Interrupts
|
|
// Maskable Hardware Interrupts
|
|
+
|
|
if (BX_CPU_THIS_PTR inhibit_mask & BX_INHIBIT_INTERRUPTS) {
|
|
// Processing external interrupts is inhibited on this
|
|
// boundary because of certain instructions like STI.
|
|
@@ -517,6 +518,16 @@
|
|
// an opportunity to check interrupts on the next instruction
|
|
// boundary.
|
|
}
|
|
+
|
|
+ else if (BX_CPU_THIS_PTR nmi_queued) {
|
|
+ BX_CPU_THIS_PTR nmi_queued = 0;
|
|
+
|
|
+ BX_CPU_THIS_PTR errorno = 0;
|
|
+ BX_CPU_THIS_PTR EXT = 1; /* external event */
|
|
+ interrupt (2, 0, 0, 0);
|
|
+ BX_INSTR_HWINTERRUPT (2, BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value, BX_CPU_THIS_PTR eip);
|
|
+ }
|
|
+
|
|
else if (BX_CPU_THIS_PTR INTR && BX_CPU_THIS_PTR eflags.if_ && BX_DBG_ASYNC_INTR) {
|
|
Bit8u vector;
|
|
|
|
Index: cpu/cpu.h
|
|
===================================================================
|
|
RCS file: /cvsroot/bochs/bochs/cpu/cpu.h,v
|
|
retrieving revision 1.19
|
|
diff -u -r1.19 cpu.h
|
|
--- cpu/cpu.h 18 Apr 2002 00:22:19 -0000 1.19
|
|
+++ cpu/cpu.h 30 May 2002 11:58:25 -0000
|
|
@@ -276,7 +276,6 @@
|
|
#endif
|
|
} bx_flags_reg_t;
|
|
|
|
-
|
|
#if BX_CPU_LEVEL >= 2
|
|
typedef struct {
|
|
Bit32u val32; // 32bit value of register
|
|
@@ -581,6 +580,7 @@
|
|
virtual ~bx_generic_apic_c ();
|
|
virtual void init ();
|
|
virtual void hwreset () { }
|
|
+ virtual BX_CPU_C *get_cpu (void);
|
|
Bit32u get_base (void) { return base_addr; }
|
|
void set_base (Bit32u newbase);
|
|
void set_id (Bit8u newid);
|
|
@@ -641,7 +641,7 @@
|
|
BX_CPU_C *cpu;
|
|
virtual void hwreset ();
|
|
virtual void init ();
|
|
- BX_CPU_C *get_cpu (Bit8u id);
|
|
+ virtual BX_CPU_C *get_cpu (void);
|
|
void set_id (Bit8u newid); // redefine to set cpu->name
|
|
virtual char *get_name();
|
|
virtual void write (Bit32u addr, Bit32u *data, unsigned len);
|
|
@@ -820,6 +820,7 @@
|
|
volatile Boolean async_event;
|
|
volatile Boolean INTR;
|
|
volatile Boolean kill_bochs_request;
|
|
+ volatile Boolean nmi_queued;
|
|
|
|
/* wether this CPU is the BSP always set for UP */
|
|
Boolean bsp;
|
|
@@ -855,8 +856,8 @@
|
|
Bit32u prev_phy_page;
|
|
Bit32u max_phy_addr;
|
|
|
|
-#if BX_DEBUGGER
|
|
Bit32u watchpoint;
|
|
+#if BX_DEBUGGER
|
|
Bit8u break_point;
|
|
#ifdef MAGIC_BREAKPOINT
|
|
Bit8u magic_break;
|
|
Index: cpu/apic.cc
|
|
===================================================================
|
|
RCS file: /cvsroot/bochs/bochs/cpu/apic.cc,v
|
|
retrieving revision 1.14
|
|
diff -u -r1.14 apic.cc
|
|
--- cpu/apic.cc 27 Mar 2002 16:04:04 -0000 1.14
|
|
+++ cpu/apic.cc 30 May 2002 11:58:25 -0000
|
|
@@ -115,6 +115,12 @@
|
|
BX_PANIC(("write not implemented in base class bx_generic_apic_c"));
|
|
}
|
|
|
|
+BX_CPU_C *bx_generic_apic_c::get_cpu (void)
|
|
+{
|
|
+ BX_PANIC(("get_cpu called on base class"));
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
void bx_generic_apic_c::startup_msg (Bit32u vector)
|
|
{
|
|
BX_PANIC(("startup message sent to an I/O APIC"));
|
|
@@ -169,32 +175,51 @@
|
|
|
|
Boolean
|
|
bx_generic_apic_c::deliver (Bit8u dest, Bit8u dest_mode, Bit8u delivery_mode, Bit8u vector, Bit8u polarity, Bit8u trig_mode)
|
|
+
|
|
{
|
|
// return false if we can't deliver for any reason, so that the caller
|
|
// knows not to clear its IRR.
|
|
+
|
|
Bit32u deliver_bitmask = get_delivery_bitmask (dest, dest_mode);
|
|
+
|
|
// mask must include ONLY local APICs, or we will have problems.
|
|
if (!deliver_bitmask) {
|
|
BX_PANIC(("deliver failed for vector %02x: no APICs in destination bitmask", vector));
|
|
return false;
|
|
}
|
|
+
|
|
switch (delivery_mode) {
|
|
case 0: // fixed
|
|
break;
|
|
case 1: // lowest priority of destinations
|
|
{
|
|
// find lowest priority of apics in the mask
|
|
- int lowest_priority = 0x100, lowest_mask = -1;
|
|
- for (int bit=0; bit<APIC_MAX_ID; bit++) {
|
|
- if (deliver_bitmask & (1<<bit)) {
|
|
- bx_local_apic_c *apic = (bx_local_apic_c *)apic_index[bit];
|
|
- if (apic->get_ppr () < lowest_priority) {
|
|
- lowest_priority = apic->get_ppr (); lowest_mask = 1<<bit;
|
|
+
|
|
+ bx_local_apic_c *apic;
|
|
+ int bit, bitidx, itsppr, lowest_bit, lowest_mask, lowest_priority;
|
|
+ static int lowest_rotate = 0; // hw probably doesn't do this but mix it up for simulation
|
|
+
|
|
+ lowest_priority = 0x100; // any priority is lower than this
|
|
+ lowest_mask = -1; // set all mask bits
|
|
+
|
|
+ for (bitidx = 0; bitidx < APIC_MAX_ID; bitidx ++) { // loop through all apic's
|
|
+ bit = (bitidx + lowest_rotate) % APIC_MAX_ID; // get absolute apic number
|
|
+ if (deliver_bitmask & (1 << bit)) { // see if delivery is enabled
|
|
+ apic = (bx_local_apic_c *)apic_index[bit]; // ok, point to the apic object
|
|
+ if (!(apic -> cpu -> debug_trap & 0x80000000)) { // see if that cpu has been started
|
|
+ itsppr = apic -> get_ppr (); // ok, get its process priority
|
|
+ itsppr &= 0xF0; // ... ignore lower 4 bits
|
|
+ if (itsppr < lowest_priority) { // if it's the lowest so far ...
|
|
+ lowest_priority = itsppr; // ... save info
|
|
+ lowest_mask = 1 << bit;
|
|
+ lowest_bit = bit;
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
- deliver_bitmask = lowest_mask;
|
|
- BX_ASSERT (deliver_bitmask >= 0);
|
|
+ deliver_bitmask = lowest_mask; // save mask bit for lowest found
|
|
+ lowest_rotate = (lowest_bit + 1) % APIC_MAX_ID; // remember this for next scan
|
|
+ BX_ASSERT (deliver_bitmask >= 0); // we must have found something
|
|
}
|
|
break;
|
|
case 5: // INIT
|
|
@@ -215,27 +240,41 @@
|
|
if (deliver_bitmask & (1<<bit))
|
|
apic_index[bit]->startup_msg (vector);
|
|
return true;
|
|
+ case 4: // NMI
|
|
+ BX_INFO (("APIC NMI deliver_bitmask %X", deliver_bitmask));
|
|
+ for (int bit=0; bit<APIC_MAX_ID; bit++) {
|
|
+ if (deliver_bitmask & (1<<bit)) {
|
|
+ BX_CPU_C *itscpu;
|
|
+ itscpu = apic_index[bit] -> get_cpu ();
|
|
+ BX_INFO (("APIC NMI bit %u, cpu %s", bit, itscpu -> name));
|
|
+ itscpu -> nmi_queued = 1;
|
|
+ itscpu -> async_event = 1;
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+
|
|
case 2: // SMI
|
|
case 3: // reserved
|
|
- case 4: // NMI
|
|
case 7: // ExtINT (I/O apic only)
|
|
default:
|
|
BX_PANIC(("APIC delivery mode %d not implemented", delivery_mode));
|
|
}
|
|
+
|
|
// Fixed delivery mode
|
|
+
|
|
if (bx_dbg.apic)
|
|
BX_INFO(("delivering vector=0x%02x to bitmask=%04x", (int)vector, deliver_bitmask));
|
|
+
|
|
for (int bit=0; bit<APIC_MAX_ID; bit++) {
|
|
if (deliver_bitmask & (1<<bit)) {
|
|
- if (apic_index[bit] == NULL)
|
|
- BX_INFO(("IOAPIC: delivering int0x%x to nonexistent id=%d!", (unsigned)vector, bit));
|
|
+ if (apic_index[bit] == NULL) BX_INFO(("IOAPIC: delivering int0x%x to nonexistent id=%d!", (unsigned)vector, bit));
|
|
else {
|
|
- if (bx_dbg.apic)
|
|
- BX_INFO(("IOAPIC: delivering int0x%x to apic#%d", (unsigned)vector, bit));
|
|
+ if (bx_dbg.apic) BX_INFO(("IOAPIC: delivering int0x%x to apic#%d", (unsigned)vector, bit));
|
|
apic_index[bit]->trigger_irq (vector, id);
|
|
}
|
|
}
|
|
}
|
|
+
|
|
return true;
|
|
}
|
|
|
|
@@ -312,9 +351,8 @@
|
|
}
|
|
|
|
BX_CPU_C
|
|
-*bx_local_apic_c::get_cpu (Bit8u id)
|
|
+*bx_local_apic_c::get_cpu (void)
|
|
{
|
|
- BX_ASSERT (id < APIC_MAX_ID);
|
|
return cpu;
|
|
}
|
|
|
|
@@ -369,16 +407,19 @@
|
|
break;
|
|
case 0x80: // task priority
|
|
task_priority = *data & 0xff;
|
|
+ service_local_apic ();
|
|
+ //print_status ();
|
|
break;
|
|
case 0xb0: // EOI
|
|
{
|
|
BX_DEBUG(("%s: Wrote 0x%04x to EOI", cpu->name, *data));
|
|
int vec = highest_priority_int (isr);
|
|
if (vec < 0) {
|
|
- BX_INFO(("EOI written without any bit in ISR"));
|
|
+ BX_INFO (("apic write*: %s: EOI written without any bit in ISR", cpu -> name));
|
|
} else {
|
|
- BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu->name, vec));
|
|
- isr[vec] = 0;
|
|
+ //BX_INFO (("apic write*: %s: EOI %d", cpu -> name, vec));
|
|
+ isr[vec] = 0;
|
|
+ //print_status ();
|
|
service_local_apic ();
|
|
}
|
|
if (bx_dbg.apic)
|
|
@@ -407,6 +448,7 @@
|
|
break;
|
|
case 0x300: // interrupt command reg 0-31
|
|
{
|
|
+ Boolean accepted;
|
|
icr_low = *data & ~(1<<12); // force delivery status bit = 0 (idle)
|
|
int dest = (icr_high >> 24) & 0xff;
|
|
int trig_mode = (icr_low >> 15) & 1;
|
|
@@ -414,15 +456,14 @@
|
|
int dest_mode = (icr_low >> 11) & 1;
|
|
int delivery_mode = (icr_low >> 8) & 7;
|
|
int vector = (icr_low & 0xff);
|
|
- //
|
|
+
|
|
// deliver will call get_delivery_bitmask to decide who to send to.
|
|
// This local_apic class redefines get_delivery_bitmask to
|
|
// implement the destination shorthand field, which doesn't exist
|
|
// for all APICs.
|
|
- Boolean accepted =
|
|
- deliver (dest, dest_mode, delivery_mode, vector, level, trig_mode);
|
|
- if (!accepted)
|
|
- err_status |= APIC_ERR_TX_ACCEPT_ERR;
|
|
+
|
|
+ accepted = deliver (dest, dest_mode, delivery_mode, vector, level, trig_mode);
|
|
+ if (!accepted) err_status |= APIC_ERR_TX_ACCEPT_ERR;
|
|
}
|
|
break;
|
|
case 0x310: // interrupt command reg 31-63
|
|
@@ -490,7 +531,7 @@
|
|
cpu->debug_trap &= ~0x80000000;
|
|
cpu->eip = 0;
|
|
cpu->load_seg_reg (&cpu->sregs[BX_SEG_REG_CS], vector*0x100);
|
|
- BX_INFO(("%s started up at 0x%x by APIC", cpu->name, cpu->eip));
|
|
+ BX_INFO(("%s started up at %04X:%08X by APIC", cpu->name, vector*0x100, cpu->eip));
|
|
} else {
|
|
BX_INFO(("%s started up by APIC, but was not halted at the time", cpu->name));
|
|
}
|
|
@@ -568,30 +609,38 @@
|
|
int
|
|
bx_local_apic_c::highest_priority_int (Bit8u *array)
|
|
{
|
|
- for (int i=0; i<BX_LOCAL_APIC_MAX_INTS; i++)
|
|
- if (array[i]) return i;
|
|
- return -1;
|
|
+ int i;
|
|
+
|
|
+ if (array[2]) return (2); // if NMI, that is the highest priority
|
|
+ for (i = 0x100; -- i >= 0;) if (array[i]) break; // all others, scan bits from highest to lowest
|
|
+ return (i);
|
|
}
|
|
|
|
void bx_local_apic_c::service_local_apic ()
|
|
{
|
|
+ int first_irr, ppr;
|
|
+
|
|
if (bx_dbg.apic) {
|
|
BX_INFO(("service_local_apic()"));
|
|
print_status ();
|
|
}
|
|
if (cpu->INTR) return; // INTR already up; do nothing
|
|
+
|
|
// find first interrupt in irr.
|
|
- int first_irr = highest_priority_int (irr);
|
|
- int first_isr = highest_priority_int (isr);
|
|
+
|
|
+ first_irr = highest_priority_int (irr); // find highest vector being requested
|
|
+ ppr = get_ppr (); // get current priorities being blocked
|
|
+
|
|
if (first_irr < 0) return; // no interrupts, leave INTR=0
|
|
- if (first_isr >= 0 && first_irr >= first_isr) {
|
|
- if (bx_dbg.apic)
|
|
- BX_INFO(("local apic (%s): not delivering int%02x because int%02x is in service", cpu->name, first_irr, first_isr));
|
|
+ if ((first_irr & 0xF0) <= (ppr & 0xF0)) {
|
|
+ if (bx_dbg.apic) BX_INFO(("local apic (%s): not delivering int %02X because int %02X is in service/blocked", cpu->name, first_irr, ppr));
|
|
return;
|
|
}
|
|
+
|
|
// interrupt has appeared in irr. raise INTR. When the CPU
|
|
// acknowledges, we will run highest_priority_int again and
|
|
// return it.
|
|
+
|
|
BX_DEBUG(("service_local_apic(): setting INTR=1 for vector 0x%02x", first_irr));
|
|
cpu->set_INTR (1);
|
|
cpu->int_from_local_apic = 1;
|
|
@@ -599,9 +648,10 @@
|
|
|
|
void bx_local_apic_c::trigger_irq (unsigned vector, unsigned from)
|
|
{
|
|
- BX_DEBUG(("Local apic on %s: trigger interrupt vector=0x%x", cpu->name, vector));
|
|
- irr[vector] = 1;
|
|
- service_local_apic ();
|
|
+ BX_DEBUG (("Local apic on %s*: trigger interrupt vector=0x%X", this->cpu->name, vector));
|
|
+ this->irr[vector] = 1;
|
|
+ //print_status ();
|
|
+ this->service_local_apic ();
|
|
}
|
|
|
|
void bx_local_apic_c::untrigger_irq (unsigned vector, unsigned from)
|
|
@@ -617,20 +667,34 @@
|
|
Bit8u
|
|
bx_local_apic_c::acknowledge_int ()
|
|
{
|
|
+ int vector, ppr;
|
|
+
|
|
// CPU calls this when it is ready to service one interrupt
|
|
- if (!cpu->INTR)
|
|
+ if (!cpu->INTR)
|
|
BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu->name));
|
|
+
|
|
BX_ASSERT (cpu->int_from_local_apic);
|
|
- int vector = highest_priority_int (irr);
|
|
- BX_ASSERT (irr[vector] == 1);
|
|
- BX_DEBUG(("%s: acknowledge_int returning vector 0x%x", cpu->name, vector));
|
|
- // currently isr never gets cleared, so no point
|
|
- //BX_ASSERT (isr[vector] == 0);
|
|
- irr[vector] = 0;
|
|
- isr[vector] = 1;
|
|
- if (bx_dbg.apic) {
|
|
- BX_INFO(("Status after setting isr:"));
|
|
- print_status ();
|
|
+ vector = highest_priority_int (irr);
|
|
+ if (vector < 0) {
|
|
+ BX_INFO (("%s: acknowledge_int w/ no interrupt requested", cpu -> name));
|
|
+ vector = spurious_vec;
|
|
+ } else {
|
|
+ ppr = get_ppr ();
|
|
+ if ((vector & 0xF0) <= (ppr & 0xF0)) {
|
|
+ BX_INFO (("%s: acknowledge_int irr 0x%02X while ppr 0x%02X", cpu -> name, vector, ppr));
|
|
+ vector = spurious_vec;
|
|
+ } else {
|
|
+ BX_ASSERT (irr[vector] == 1);
|
|
+ BX_DEBUG(("%s: acknowledge_int returning vector 0x%x", cpu->name, vector));
|
|
+
|
|
+ irr[vector] = 0;
|
|
+ isr[vector] = 1;
|
|
+ //print_status ();
|
|
+ if (bx_dbg.apic) {
|
|
+ BX_INFO(("Status after setting isr:"));
|
|
+ print_status ();
|
|
+ }
|
|
+ }
|
|
}
|
|
cpu->INTR = 0;
|
|
cpu->int_from_local_apic = 0;
|
|
@@ -639,10 +703,10 @@
|
|
}
|
|
|
|
void bx_local_apic_c::print_status () {
|
|
- BX_INFO(("%s local apic: status is {:", cpu->name));
|
|
+ BX_INFO(("%s local apic: tpr 0x%02X, ppr 0x%02X, status is {:", cpu->name, task_priority, get_ppr ()));
|
|
for (int vec=0; vec<BX_LOCAL_APIC_MAX_INTS; vec++) {
|
|
if (irr[vec] || isr[vec]) {
|
|
- BX_INFO(("vec 0x%x: irr=%d, isr=%d", vec, (int)irr[vec], (int)isr[vec]));
|
|
+ BX_INFO((" vec 0x%02X: irr=%d, isr=%d", vec, (int)irr[vec], (int)isr[vec]));
|
|
}
|
|
}
|
|
BX_INFO(("}", cpu->name));
|
|
@@ -691,14 +755,14 @@
|
|
}
|
|
|
|
Bit8u bx_local_apic_c::get_ppr ()
|
|
+
|
|
{
|
|
- static int warned = 0;
|
|
- if (warned < 10) {
|
|
- BX_ERROR(("WARNING: Local APIC Processor Priority not implemented, returning 0"));
|
|
- warned++;
|
|
- }
|
|
- // should look at TPR, vector of highest priority isr, etc.
|
|
- return 0;
|
|
+ int ppr;
|
|
+
|
|
+ ppr = highest_priority_int (isr); // see what's in service now
|
|
+ if ((ppr < 0) || ((task_priority & 0xF0) >= (ppr & 0xF0))) ppr = task_priority; // or if tpr is higher, use that
|
|
+ else ppr &= 0xF0;
|
|
+ return (ppr);
|
|
}
|
|
|
|
|