ba6afa5e97
- moved cpu_online_map into the BX_CPU_C structure as a static member (there is only one per bochs, not one per CPU) - reduced the diffs in several places to make it more clear what had changed - removed lots of whitespace diffs
1016 lines
31 KiB
Plaintext
1016 lines
31 KiB
Plaintext
----------------------------------------------------------------------
|
|
Patch name: patch.apic.zwane
|
|
Author: Zwane Mwaikambo <zwane@linuxpower.ca>
|
|
Date: Wed, 25 Sep 2002 07:59:37 -0400 (EDT)
|
|
|
|
Detailed description:
|
|
|
|
Hi this patch adds the following;
|
|
|
|
o Define symbols for constants like
|
|
o APIC arbitration
|
|
o Processor priority
|
|
o Various interrupt delivery fixes
|
|
o Focus processor checking
|
|
o ExtINT delivery
|
|
|
|
I need to release this now so that i don't fall too far behind CVS, when
|
|
it was part of the bochs-smp patch it could boot 2.4.18 4way. Apologies
|
|
for the whitespace changes.
|
|
|
|
Changes by Bryce:
|
|
- moved cpu_online_map into the BX_CPU_C structure as a static member
|
|
(there is only one per bochs, not one per CPU)
|
|
- reduced the diffs in several places to make it more clear what had changed
|
|
- removed lots of whitespace diffs
|
|
|
|
|
|
Patch was created with:
|
|
cvs diff -u
|
|
Apply patch to what version:
|
|
cvs checked out on DATE, release version VER
|
|
Instructions:
|
|
To patch, go to main bochs directory.
|
|
Type "patch -p1 < THIS_PATCH_FILE".
|
|
----------------------------------------------------------------------
|
|
|
|
Index: cpu/apic.cc
|
|
===================================================================
|
|
RCS file: /cvsroot/bochs/bochs/cpu/apic.cc,v
|
|
retrieving revision 1.18
|
|
diff -u -r1.18 apic.cc
|
|
--- cpu/apic.cc 25 Sep 2002 01:50:14 -0000 1.18
|
|
+++ cpu/apic.cc 25 Sep 2002 14:41:04 -0000
|
|
@@ -8,6 +8,7 @@
|
|
#define LOG_THIS this->
|
|
|
|
bx_generic_apic_c *apic_index[APIC_MAX_ID];
|
|
+bx_local_apic_c *local_apic_index[BX_LOCAL_APIC_NUM];
|
|
|
|
bx_generic_apic_c::bx_generic_apic_c ()
|
|
{
|
|
@@ -139,6 +140,70 @@
|
|
return APIC_TYPE_NONE;
|
|
}
|
|
|
|
+/* apic_mask is the bitmask of apics allowed to arbitrate here */
|
|
+int bx_generic_apic_c::apic_bus_arbitrate(Bit32u apic_mask)
|
|
+{
|
|
+ int winning_apr = 0, winning_id = 0, __apr, i;
|
|
+ for (i = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (apic_mask & (1<<i)) {
|
|
+ __apr = local_apic_index[i]->get_apr();
|
|
+ if (__apr > winning_apr) {
|
|
+ winning_apr = __apr;
|
|
+ winning_id = i;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return winning_id;
|
|
+}
|
|
+
|
|
+/* get the CPU with the lowest arbitration ID */
|
|
+int bx_generic_apic_c::apic_bus_arbitrate_lowpri(Bit32u apic_mask)
|
|
+{
|
|
+ // XXX initial winning_apr value, the datasheets say 15
|
|
+ int winning_apr = APIC_MAX_ID, winning_id = 0 , __apr, i;
|
|
+ for (i = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (apic_mask & (1<<i)) {
|
|
+ __apr = local_apic_index[i]->get_apr_lowpri();
|
|
+ if (__apr < winning_apr) {
|
|
+ winning_apr = __apr;
|
|
+ winning_id = i;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return winning_id;
|
|
+}
|
|
+
|
|
+void bx_generic_apic_c::arbitrate_and_trigger(Bit32u deliver_bitmask, Bit32u vector, Bit8u trigger_mode)
|
|
+{
|
|
+ int trigger_order[BX_LOCAL_APIC_NUM], winner, i, j;
|
|
+#define TERMINATE_MAGIK 0x5a
|
|
+
|
|
+ /* bus arbitrate ... */
|
|
+ for (i = 0, j = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (deliver_bitmask & (1<<i)) {
|
|
+ winner = apic_bus_arbitrate(deliver_bitmask);
|
|
+ local_apic_index[winner]->adjust_arb_id(winner);
|
|
+ trigger_order[j++] = winner;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (j < BX_LOCAL_APIC_NUM)
|
|
+ trigger_order[j] = TERMINATE_MAGIK;
|
|
+
|
|
+ i = 0;
|
|
+ do {
|
|
+ local_apic_index[trigger_order[i]]->trigger_irq(vector, trigger_order[i], trigger_mode);
|
|
+ i++;
|
|
+ } while (trigger_order[i] != TERMINATE_MAGIK);
|
|
+}
|
|
+
|
|
+void bx_generic_apic_c::arbitrate_and_trigger_one(Bit32u deliver_bitmask, Bit32u vector, Bit8u trigger_mode)
|
|
+{
|
|
+ int winner = apic_bus_arbitrate(deliver_bitmask);
|
|
+ local_apic_index[winner]->adjust_arb_id(winner);
|
|
+ local_apic_index[winner]->trigger_irq(vector, winner, trigger_mode);
|
|
+}
|
|
+
|
|
Bit32u
|
|
bx_generic_apic_c::get_delivery_bitmask (Bit8u dest, Bit8u dest_mode)
|
|
{
|
|
@@ -156,8 +221,8 @@
|
|
} else {
|
|
// logical destination. call match_logical_addr for each APIC.
|
|
if (dest == 0) return 0;
|
|
- for (int i=0; i<APIC_MAX_ID; i++) {
|
|
- if (apic_index[i] && apic_index[i]->match_logical_addr(dest))
|
|
+ for (int i=0; i<BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (local_apic_index[i]->match_logical_addr(dest))
|
|
mask |= (1<<i);
|
|
}
|
|
}
|
|
@@ -167,85 +232,121 @@
|
|
}
|
|
|
|
Boolean
|
|
-bx_generic_apic_c::deliver (Bit8u dest, Bit8u dest_mode, Bit8u delivery_mode, Bit8u vector, Bit8u polarity, Bit8u trig_mode)
|
|
+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);
|
|
+ int lowest_priority = 0x100, lowest_mask = -1;
|
|
+ // arbitrate by default
|
|
+ int arbitrate = 1;
|
|
+ int broadcast = deliver_bitmask == BX_CPU_C::cpu_online_map;
|
|
+ Boolean once = false;
|
|
+
|
|
// 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;
|
|
- }
|
|
- }
|
|
- }
|
|
- deliver_bitmask = lowest_mask;
|
|
- BX_ASSERT (deliver_bitmask >= 0);
|
|
+ case APIC_DM_LOWPRI:
|
|
+ /* fall through, we've already done low priority arbitration */
|
|
+ arbitrate = 0;
|
|
+ case APIC_DM_FIXED:
|
|
+ /* once = false */
|
|
+ break;
|
|
+ case APIC_DM_INIT:
|
|
+ // normal INIT IPI sent to processors
|
|
+ int i;
|
|
+ for (i = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (deliver_bitmask & (1<<i))
|
|
+ local_apic_index[i]->init();
|
|
}
|
|
+ // HACK!! We need to do some IOAPIC init after the CPUs
|
|
+ // are fired up
|
|
+ apic_index[i]->init();
|
|
+ return true;
|
|
+
|
|
+ case APIC_DM_EXTINT:
|
|
+ for (int i = 0; i < BX_LOCAL_APIC_NUM; i++)
|
|
+ if (deliver_bitmask & (1<<i))
|
|
+ local_apic_index[i]->bypass_irr_isr = true;
|
|
break;
|
|
- case 5: // INIT
|
|
- {
|
|
- // NOTE: special behavior of local apics is handled in
|
|
- // bx_local_apic_c::deliver.
|
|
|
|
- // normal INIT. initialize the local apics in the delivery mask.
|
|
- for (int bit=0; bit<APIC_MAX_ID; bit++) {
|
|
- if (deliver_bitmask & (1<<bit))
|
|
- apic_index[bit]->init ();
|
|
- }
|
|
- }
|
|
- return true;
|
|
- case 6: // Start Up (local apic only)
|
|
- BX_ASSERT (get_type () == APIC_TYPE_LOCAL_APIC);
|
|
- for (int bit=0; bit<APIC_MAX_ID; bit++)
|
|
- if (deliver_bitmask & (1<<bit))
|
|
- apic_index[bit]->startup_msg (vector);
|
|
- return true;
|
|
- case 2: // SMI
|
|
+ case APIC_DM_SMI:
|
|
+ case APIC_DM_NMI:
|
|
case 3: // reserved
|
|
- case 4: // NMI
|
|
- case 7: // ExtINT (I/O apic only)
|
|
default:
|
|
BX_PANIC(("APIC delivery mode %d not implemented", delivery_mode));
|
|
+ return false;
|
|
}
|
|
- // 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));
|
|
+ BX_INFO(("delivered vector=0x%02x to bitmask=%04x", (int)vector, deliver_bitmask));
|
|
+
|
|
+ // delivery only to one APIC
|
|
+ if (once) {
|
|
+ if (arbitrate)
|
|
+ arbitrate_and_trigger_one(deliver_bitmask, vector, trig_mode);
|
|
else {
|
|
- if (bx_dbg.apic)
|
|
- BX_INFO(("IOAPIC: delivering int0x%x to apic#%d", (unsigned)vector, bit));
|
|
- apic_index[bit]->trigger_irq (vector, id);
|
|
+ for (int i = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (deliver_bitmask & (1<<i)) {
|
|
+ local_apic_index[i]->trigger_irq(vector, i, trig_mode);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (arbitrate && !broadcast)
|
|
+ arbitrate_and_trigger(deliver_bitmask, vector, trig_mode);
|
|
+ else {
|
|
+ for (int i = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (deliver_bitmask & (1<<i))
|
|
+ local_apic_index[i]->trigger_irq(vector, i, trig_mode);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
-Boolean
|
|
-bx_local_apic_c::deliver (Bit8u dest, Bit8u dest_mode, Bit8u delivery_mode, Bit8u vector, Bit8u polarity, Bit8u trig_mode)
|
|
+
|
|
+Boolean bx_local_apic_c::deliver (Bit8u dest, Bit8u dest_mode, Bit8u delivery_mode,
|
|
+ Bit8u vector, Bit8u polarity, Bit8u trig_mode)
|
|
{
|
|
// In this function, implement only the behavior that is specific to
|
|
// the local apic. For general behavior of all apics, just send it to
|
|
// the base class.
|
|
- if (delivery_mode == 5)
|
|
- {
|
|
+ Bit32u deliver_bitmask = get_delivery_bitmask (dest, dest_mode);
|
|
+ int found_focus = 0;
|
|
+ int broadcast = deliver_bitmask == BX_CPU_C::cpu_online_map;
|
|
+
|
|
+ if (broadcast)
|
|
+ BX_INFO(("Broadcast IPI for vector %#x delivery_mode %#x", vector, delivery_mode));
|
|
+ switch (delivery_mode) {
|
|
+ case APIC_DM_LOWPRI: // lowest priority of destinations
|
|
+ // if we're focus processor, handle it, otherwise
|
|
+ // look for the focus processor.
|
|
+ dest = is_focus(vector) ? get_id() : 0;
|
|
+ if (dest)
|
|
+ break;
|
|
+
|
|
+ for (int i = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (local_apic_index[i]->is_focus(vector) == true) {
|
|
+ found_focus = 1;
|
|
+ dest = i;
|
|
+ break; // stop scanning
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!found_focus)
|
|
+ dest = apic_bus_arbitrate_lowpri(0xff);
|
|
+ else
|
|
+ return false;
|
|
+ break;
|
|
+ case APIC_DM_INIT: {
|
|
+ int bit;
|
|
int trig_mode = (icr_low >> 15) & 1;
|
|
int level = (icr_low >> 14) & 1;
|
|
if (level == 0 && trig_mode == 1) {
|
|
@@ -254,13 +355,26 @@
|
|
// causes all APICs (regardless of dest address) to set their
|
|
// arbitration ID to their APIC ID.
|
|
BX_INFO (("INIT with Level&Deassert: synchronize arbitration IDs"));
|
|
- for (int bit=0; bit<APIC_MAX_ID; bit++) {
|
|
- if (apic_index[bit])
|
|
- apic_index[bit]->set_arb_id (apic_index[bit]->get_id ());
|
|
- }
|
|
+ for (bit=0; bit<BX_LOCAL_APIC_NUM; bit++)
|
|
+ local_apic_index[bit]->set_arb_id(local_apic_index[bit]->get_id());
|
|
+
|
|
+ apic_index[bit]->set_arb_id(apic_index[bit]->get_id()); // HACK !!!
|
|
return true;
|
|
}
|
|
+ break; // we'll fall through to generic_deliver:case INIT
|
|
+ }
|
|
+
|
|
+ case APIC_DM_SIPI: // Start Up (SIPI, local apic only)
|
|
+ for (int bit=0; bit<BX_LOCAL_APIC_NUM; bit++) {
|
|
+ if (deliver_bitmask & (1<<bit))
|
|
+ local_apic_index[bit]->startup_msg(vector);
|
|
+ }
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
}
|
|
+
|
|
// not any special case behavior, just use generic apic code.
|
|
return bx_generic_apic_c::deliver (dest, dest_mode, delivery_mode, vector, polarity, trig_mode);
|
|
}
|
|
@@ -289,6 +403,7 @@
|
|
object, do not mess around with it */
|
|
// id = APIC_UNKNOWN_ID;
|
|
arb_id = id;
|
|
+ BX_CPU_C::cpu_online_map |= (1 << id);
|
|
}
|
|
|
|
void
|
|
@@ -297,9 +412,12 @@
|
|
bx_generic_apic_c::init ();
|
|
BX_INFO(("local apic in %s initializing",
|
|
(cpu && cpu->name) ? cpu->name : "?"));
|
|
+ local_apic_index[id] = this;
|
|
+
|
|
// default address for a local APIC, can be moved
|
|
base_addr = APIC_BASE_ADDR;
|
|
update_msr_apicbase(base_addr);
|
|
+ bypass_irr_isr = false;
|
|
err_status = 0;
|
|
log_dest = 0;
|
|
dest_format = 0xf;
|
|
@@ -322,10 +440,13 @@
|
|
// nothing for now
|
|
}
|
|
|
|
-void bx_local_apic_c::set_id (Bit8u newid) {
|
|
+void bx_local_apic_c::set_id (Bit8u newid)
|
|
+{
|
|
bx_generic_apic_c::set_id (newid);
|
|
+ local_apic_index[id] = this;
|
|
+
|
|
sprintf (cpu->name, "CPU apicid=%02x", (Bit32u)id);
|
|
- if (id <= 15) {
|
|
+ if (id < APIC_MAX_ID) {
|
|
char buffer[16];
|
|
sprintf (buffer, "APIC%x", id);
|
|
put(buffer);
|
|
@@ -333,12 +454,8 @@
|
|
sprintf (buffer, "CPU%x", id);
|
|
cpu->put (buffer);
|
|
} else {
|
|
- BX_INFO (("naming convention for apics requires id=0-15 only"));
|
|
+ BX_INFO (("naming convention for apics requires id=0-%d only", APIC_MAX_ID));
|
|
}
|
|
- if(BX_CPU_LEVEL<2)
|
|
- BX_INFO(( "8086" ));
|
|
- else
|
|
- BX_INFO(( "80%d86", BX_CPU_LEVEL ));
|
|
}
|
|
|
|
char *
|
|
@@ -365,7 +482,7 @@
|
|
addr &= 0xff0;
|
|
switch (addr) {
|
|
case 0x20: // local APIC id
|
|
- id = ((*data)>>24) & 0xf;
|
|
+ id = ((*data)>>24) & APIC_ID_MASK;
|
|
break;
|
|
case 0x80: // task priority
|
|
task_priority = *data & 0xff;
|
|
@@ -386,7 +503,7 @@
|
|
}
|
|
break;
|
|
case 0xd0: // logical destination
|
|
- log_dest = (*data >> 24) & 0xff;
|
|
+ log_dest = (*data >> 24) & APIC_ID_MASK;
|
|
BX_DEBUG (("set logical destiation to %02x", log_dest));
|
|
break;
|
|
case 0xe0: // destination format
|
|
@@ -522,21 +639,32 @@
|
|
*/
|
|
break;
|
|
case 0xd0: // logical destination
|
|
- *data = (log_dest & 0xff) << 24; break;
|
|
+ *data = (log_dest & APIC_ID_MASK) << 24; break;
|
|
case 0xe0: // destination format
|
|
*data = ((dest_format & 0xf) << 24) | 0x0fffffff; break;
|
|
case 0xf0: // spurious interrupt vector
|
|
*data = spurious_vec; break;
|
|
- // ISRs not writable
|
|
- case 0x100: case 0x110: case 0x120: case 0x130:
|
|
- case 0x140: case 0x150: case 0x160: case 0x170:
|
|
- case 0x180: case 0x190: case 0x1a0: case 0x1b0:
|
|
- case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
|
|
- case 0x200: case 0x210: case 0x220: case 0x230:
|
|
- case 0x240: case 0x250: case 0x260: case 0x270:
|
|
- *data = 0;
|
|
- BX_INFO(("reading ISR,TMR,IRR not implemented"));
|
|
+
|
|
+ // XXX blearrchhaarghh!!
|
|
+ case 0x100: case 0x110:
|
|
+ case 0x120: case 0x130:
|
|
+ case 0x140: case 0x150:
|
|
+ case 0x160: case 0x170:
|
|
+ *data = isr[addr2-0x100];
|
|
+ break;
|
|
+ case 0x180: case 0x190:
|
|
+ case 0x1a0: case 0x1b0:
|
|
+ case 0x1c0: case 0x1d0:
|
|
+ case 0x1e0: case 0x1f0:
|
|
+ *data = tmr[addr2-0x180];
|
|
+ break;
|
|
+ case 0x200: case 0x210:
|
|
+ case 0x220: case 0x230:
|
|
+ case 0x240: case 0x250:
|
|
+ case 0x260: case 0x270:
|
|
+ *data = irr[addr2-0x200];
|
|
break;
|
|
+
|
|
case 0x280: // error status reg
|
|
*data = err_status; break;
|
|
case 0x300: // interrupt command reg 0-31
|
|
@@ -577,18 +705,28 @@
|
|
|
|
void bx_local_apic_c::service_local_apic ()
|
|
{
|
|
+ int first_irr, first_isr;
|
|
+
|
|
if (bx_dbg.apic) {
|
|
BX_INFO(("service_local_apic()"));
|
|
print_status ();
|
|
}
|
|
- if (cpu->INTR) return; // INTR already up; do nothing
|
|
+
|
|
+ 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);
|
|
- if (first_irr < 0) return; // no interrupts, leave INTR=0
|
|
+ first_irr = highest_priority_int (irr);
|
|
+ first_isr = highest_priority_int (isr);
|
|
+ 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));
|
|
+ BX_INFO(("not delivering int%02x because int%02x is in service",
|
|
+ cpu->name, first_irr, first_isr));
|
|
+ err_status |= APIC_ERR_TX_ACCEPT_ERR;
|
|
+
|
|
return;
|
|
}
|
|
// interrupt has appeared in irr. raise INTR. When the CPU
|
|
@@ -599,21 +737,45 @@
|
|
cpu->int_from_local_apic = 1;
|
|
}
|
|
|
|
-void bx_local_apic_c::trigger_irq (unsigned vector, unsigned from)
|
|
+void bx_local_apic_c::trigger_irq (unsigned vector, unsigned from, unsigned trigger_mode)
|
|
{
|
|
- BX_DEBUG(("Local apic on %s: trigger interrupt vector=0x%x", cpu->name, vector));
|
|
+ /* check for local/apic_index usage */
|
|
+ BX_ASSERT(from == id);
|
|
+
|
|
+ if (vector > BX_APIC_LAST_VECTOR) {
|
|
+ err_status |= APIC_ERR_RX_ILLEGAL_VEC;
|
|
+ BX_INFO(("bogus vector %#x, ignoring", vector));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (bx_dbg.apic)
|
|
+ BX_INFO(("triggered vector %#02x", vector));
|
|
+
|
|
+ if (bypass_irr_isr) {
|
|
+ bypass_irr_isr = false;
|
|
+ goto service_vector;
|
|
+ }
|
|
+
|
|
+ if (irr[vector] != 0) {
|
|
+ err_status |= APIC_ERR_TX_ACCEPT_ERR;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+service_vector:
|
|
irr[vector] = 1;
|
|
+ tmr[vector] = trigger_mode; // set for level triggered
|
|
service_local_apic ();
|
|
}
|
|
|
|
-void bx_local_apic_c::untrigger_irq (unsigned vector, unsigned from)
|
|
+void bx_local_apic_c::untrigger_irq (unsigned vector, unsigned from, unsigned trigger_mode)
|
|
{
|
|
BX_DEBUG(("Local apic on %s: untrigger interrupt vector=0x%x", cpu->name, vector));
|
|
// hardware says "no more". clear the bit. If the CPU hasn't yet
|
|
// acknowledged the interrupt, it will never be serviced.
|
|
BX_ASSERT (irr[vector] == 1);
|
|
irr[vector] = 0;
|
|
- if (bx_dbg.apic) print_status ();
|
|
+ if (bx_dbg.apic)
|
|
+ print_status ();
|
|
}
|
|
|
|
Bit8u
|
|
@@ -622,14 +784,21 @@
|
|
// CPU calls this when it is ready to service one interrupt
|
|
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);
|
|
+ /*XXX */
|
|
+ if (irr[vector] != 1) {
|
|
+ BX_INFO(("IRR was not 1! irr[%d]=%#x", vector, irr[vector]));
|
|
+ irr[vector]=1;
|
|
+ }
|
|
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 ();
|
|
@@ -643,26 +812,21 @@
|
|
void bx_local_apic_c::print_status () {
|
|
BX_INFO(("%s local apic: status is {:", cpu->name));
|
|
for (int vec=0; vec<BX_LOCAL_APIC_MAX_INTS; vec++) {
|
|
- if (irr[vec] || isr[vec]) {
|
|
+ if (irr[vec] || isr[vec])
|
|
BX_INFO(("vec 0x%x: irr=%d, isr=%d", vec, (int)irr[vec], (int)isr[vec]));
|
|
}
|
|
- }
|
|
- BX_INFO(("}", cpu->name));
|
|
+ BX_INFO(("}"));
|
|
}
|
|
|
|
Boolean bx_local_apic_c::match_logical_addr (Bit8u address)
|
|
{
|
|
- if (dest_format != 0xf) {
|
|
- BX_PANIC(("bx_local_apic_c::match_logical_addr: cluster model addressing not implemented"));
|
|
- }
|
|
- // if all address bits are 1, send to all local APICs. SDG3:7-27.
|
|
- if (address == 0xff) {
|
|
- BX_DEBUG (("%s: MDA=0xff matches everybody", cpu->name));
|
|
- return true;
|
|
- }
|
|
+ if (dest_format != 0xf)
|
|
+ BX_PANIC(("cluster model addressing not implemented"));
|
|
+
|
|
Boolean match = ((address & log_dest) != 0);
|
|
BX_DEBUG (("%s: comparing MDA %02x to my LDR %02x -> %s", cpu->name,
|
|
address, log_dest, match? "Match" : "Not a match"));
|
|
+
|
|
return match;
|
|
}
|
|
|
|
@@ -670,68 +834,121 @@
|
|
bx_local_apic_c::get_delivery_bitmask (Bit8u dest, Bit8u dest_mode)
|
|
{
|
|
int dest_shorthand = (icr_low >> 18) & 3;
|
|
- Bit32u all_mask = (1<<APIC_MAX_ID) - 1;
|
|
Bit32u mask;
|
|
+
|
|
switch (dest_shorthand) {
|
|
case 0: // no shorthand, use real destination value
|
|
- return bx_generic_apic_c::get_delivery_bitmask (dest, dest_mode);
|
|
+ mask = bx_generic_apic_c::get_delivery_bitmask (dest, dest_mode);
|
|
+ break;
|
|
case 1: // self
|
|
- return (1<<id);
|
|
+ mask = (1<<id);
|
|
+ break;
|
|
case 2: // all including self
|
|
- mask = all_mask;
|
|
+ mask = BX_CPU_C::cpu_online_map;
|
|
+ break;
|
|
case 3: // all but self
|
|
- mask = all_mask & ~(1<<id);
|
|
+ mask = BX_CPU_C::cpu_online_map & ~(1<<id);
|
|
+ break;
|
|
+ default:
|
|
+ BX_PANIC(("Invalid desination shorthand %#x\n", dest_shorthand));
|
|
}
|
|
- // prune nonexistents and I/O apics from list
|
|
- for (int bit=0; bit<APIC_MAX_ID; bit++) {
|
|
- if (!apic_index[bit]
|
|
- || (apic_index[bit]->get_type () != APIC_TYPE_LOCAL_APIC))
|
|
- mask &= ~(1<<bit);
|
|
- }
|
|
- BX_DEBUG (("local::get_delivery_bitmask returning 0x%04x", mask));
|
|
+
|
|
+ BX_DEBUG (("local::get_delivery_bitmask returning 0x%04x shorthand=%#x", mask, dest_shorthand));
|
|
+ if (mask == 0)
|
|
+ BX_INFO((">>WARNING<< returning a mask of 0x0, dest=%#x dest_mode=%#x", dest, dest_mode));
|
|
+
|
|
return mask;
|
|
}
|
|
|
|
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;
|
|
-}
|
|
+ Bit32u tpr = (task_priority >> 4) & 0xf; /* we want 7:4 */
|
|
+ Bit32u isrv = (highest_priority_int(isr) >> 4) & 0xf; /* ditto */
|
|
+
|
|
+ if (tpr >= isrv)
|
|
+ proc_priority = task_priority & 0xff;
|
|
+ else
|
|
+ proc_priority = isrv << 4; /* low 4 bits of PPR have to be cleared */
|
|
|
|
+ if (bx_dbg.apic)
|
|
+ BX_DEBUG(("%s: get_ppr returning %#x", cpu->name, proc_priority));
|
|
+
|
|
+ return (Bit8u)proc_priority;
|
|
+}
|
|
|
|
Bit8u bx_local_apic_c::get_apr ()
|
|
{
|
|
return arb_id;
|
|
}
|
|
|
|
+Bit8u bx_local_apic_c::get_apr_lowpri()
|
|
+{
|
|
+ Bit32u tpr = (task_priority >> 4) & 0xf;
|
|
+ Bit32u isrv = (highest_priority_int(isr) >> 4) & 0xf;
|
|
+ Bit32u irrv = (highest_priority_int(irr) >> 4) & 0xf;
|
|
+
|
|
+ if ((tpr >= irrv) && (tpr > isrv))
|
|
+ arb_id = task_priority & 0xff;
|
|
+ else
|
|
+ arb_id = ((tpr && isrv) > irrv) ? (tpr && isrv) : irrv;
|
|
+
|
|
+ BX_INFO(("apr = %d\n", arb_id));
|
|
+ return (Bit8u)arb_id;
|
|
+}
|
|
+
|
|
+Boolean bx_local_apic_c::is_focus(Bit32u vector)
|
|
+{
|
|
+ return (irr[vector] || isr[vector]) ? true : false;
|
|
+}
|
|
+
|
|
+void bx_local_apic_c::adjust_arb_id(int winning_id)
|
|
+{
|
|
+ int __apr, __win_apr;
|
|
+
|
|
+ // adjust arbitration priorities
|
|
+ for (int i = 0; i < BX_LOCAL_APIC_NUM; i++) {
|
|
+ if (i != winning_id) {
|
|
+ __apr = local_apic_index[i]->get_apr();
|
|
+ if (__apr == 15) {
|
|
+ __win_apr = local_apic_index[winning_id]->get_apr();
|
|
+ local_apic_index[i]->set_arb_id(__win_apr+1);
|
|
+ } else
|
|
+ local_apic_index[i]->set_arb_id(__apr+1);
|
|
+ } else
|
|
+ local_apic_index[winning_id]->set_arb_id(0); // the winner drops to lowest
|
|
+ }
|
|
+}
|
|
+
|
|
void
|
|
bx_local_apic_c::periodic (Bit32u usec_delta)
|
|
{
|
|
- if (!timer_active) return;
|
|
+ if (!timer_active)
|
|
+ return;
|
|
+
|
|
BX_DEBUG(("%s: bx_local_apic_c::periodic called with %d usec",
|
|
cpu->name, usec_delta));
|
|
+
|
|
// unless usec_delta is guaranteed to be a multiple of 128, I can't
|
|
// just divide usec_delta by the divide-down value. Instead, it will
|
|
// have a similar effect to implement the divide-down by ignoring
|
|
// some fraction of calls to this function. This can be improved if
|
|
// more granularity is important.
|
|
+
|
|
timer_divide_counter = (timer_divide_counter + 1) % timer_divide_factor;
|
|
- if (timer_divide_counter != 0) return;
|
|
+ if (timer_divide_counter != 0)
|
|
+ return;
|
|
+
|
|
if (timer_current > usec_delta) {
|
|
timer_current -= usec_delta;
|
|
- //BX_INFO(("%s: local apic timer is now 0x%08x", cpu->name, timer_current));
|
|
+ BX_DEBUG(("%s: local apic timer is now 0x%08x", cpu->name, timer_current));
|
|
return;
|
|
}
|
|
+
|
|
// timer reached zero since the last call to periodic.
|
|
Bit32u timervec = lvt[APIC_LVT_TIMER];
|
|
if (timervec & 0x20000) {
|
|
// periodic mode. Always trigger the interrupt when we reach zero.
|
|
- trigger_irq (timervec & 0xff, id);
|
|
+ trigger_irq (timervec & 0xff, id, APIC_EDGE_TRIGGERED);
|
|
if (timer_initial == 0) {
|
|
usec_delta = 0;
|
|
timer_current = 0;
|
|
@@ -745,14 +962,17 @@
|
|
// negative timer_current.
|
|
BX_ASSERT ((timer_current + timer_initial) >= usec_delta);
|
|
}
|
|
- BX_DEBUG(("%s: local apic timer (periodic) triggered int, reset counter to 0x%08x", cpu->name, timer_current));
|
|
+
|
|
+ BX_DEBUG(("%s: local apic timer (periodic) triggered int, reset counter to 0x%08x",
|
|
+ cpu->name, timer_current));
|
|
} else {
|
|
// one-shot mode
|
|
timer_current = 0;
|
|
if (timer_active) {
|
|
- trigger_irq (timervec & 0xff, id);
|
|
+ trigger_irq (timervec & 0xff, id, APIC_EDGE_TRIGGERED);
|
|
timer_active = false;
|
|
BX_DEBUG (("%s: local apic timer (one-shot) triggered int", cpu->name));
|
|
}
|
|
}
|
|
}
|
|
+
|
|
Index: cpu/cpu.cc
|
|
===================================================================
|
|
RCS file: /cvsroot/bochs/bochs/cpu/cpu.cc,v
|
|
retrieving revision 1.52
|
|
diff -u -r1.52 cpu.cc
|
|
--- cpu/cpu.cc 24 Sep 2002 18:33:37 -0000 1.52
|
|
+++ cpu/cpu.cc 25 Sep 2002 14:41:05 -0000
|
|
@@ -65,6 +65,9 @@
|
|
};
|
|
#endif
|
|
|
|
+#if BX_SUPPORT_APIC
|
|
+Bit32u BX_CPU_C::cpu_online_map = 0;
|
|
+#endif
|
|
|
|
#if BX_SMP_PROCESSORS==1
|
|
// single processor simulation, so there's one of everything
|
|
Index: cpu/cpu.h
|
|
===================================================================
|
|
RCS file: /cvsroot/bochs/bochs/cpu/cpu.h,v
|
|
retrieving revision 1.80
|
|
diff -u -r1.80 cpu.h
|
|
--- cpu/cpu.h 25 Sep 2002 13:26:04 -0000 1.80
|
|
+++ cpu/cpu.h 25 Sep 2002 14:41:08 -0000
|
|
@@ -1153,13 +1153,21 @@
|
|
|
|
#define APIC_BASE_ADDR 0xfee00000 // default APIC address
|
|
|
|
+#if BX_CPU_LEVEL == 5
|
|
+# define APIC_VERSION_ID 0x00340011
|
|
+#else
|
|
+# define APIC_VERSION_ID 0x00040011 // P6
|
|
+#endif
|
|
+
|
|
+#define IOAPIC_VERSION_ID 0x00170011 // same version as 82093 IOAPIC
|
|
+
|
|
+
|
|
#if BX_SUPPORT_APIC
|
|
class bx_generic_apic_c : public logfunctions {
|
|
protected:
|
|
Bit32u base_addr;
|
|
Bit8u id;
|
|
#define APIC_UNKNOWN_ID 0xff
|
|
-#define APIC_VERSION_ID 0x00170011 // same version as 82093 IOAPIC
|
|
public:
|
|
bx_generic_apic_c ();
|
|
virtual ~bx_generic_apic_c ();
|
|
@@ -1184,10 +1192,20 @@
|
|
virtual Boolean match_logical_addr (Bit8u address);
|
|
virtual bx_apic_type_t get_type ();
|
|
virtual void set_arb_id (int newid); // only implemented on local apics
|
|
+ int apic_bus_arbitrate(Bit32u apic_mask);
|
|
+ int apic_bus_arbitrate_lowpri(Bit32u apic_mask);
|
|
+ void arbitrate_and_trigger(Bit32u deliver_bitmask, Bit32u vector, Bit8u trigger_mode);
|
|
+ void arbitrate_and_trigger_one(Bit32u deliver_bitmask, Bit32u vector, Bit8u trigger_mode);
|
|
};
|
|
|
|
class bx_local_apic_c : public bx_generic_apic_c {
|
|
+#define BX_LOCAL_APIC_NUM BX_SMP_PROCESSORS
|
|
+#define BX_APIC_FIRST_VECTOR 0x10
|
|
+#define BX_APIC_LAST_VECTOR 0xfe
|
|
#define BX_LOCAL_APIC_MAX_INTS 256
|
|
+
|
|
+#define APIC_LEVEL_TRIGGERED 1
|
|
+#define APIC_EDGE_TRIGGERED 0
|
|
// TMR=trigger mode register. Cleared for edge-triggered interrupts
|
|
// and set for level-triggered interrupts. If set, local APIC must send
|
|
// EOI message to all other APICs. EOI's are not implemented.
|
|
@@ -1199,7 +1217,7 @@
|
|
// ISR=in-service register. When an IRR bit is cleared, the corresponding
|
|
// bit in ISR is set. The ISR bit is cleared when
|
|
Bit8u isr[BX_LOCAL_APIC_MAX_INTS];
|
|
- Bit32u arb_id, arb_priority, task_priority, log_dest, dest_format, spurious_vec;
|
|
+ Bit32u arb_id, arb_priority, task_priority, proc_priority, log_dest, dest_format, spurious_vec;
|
|
Bit32u lvt[6];
|
|
#define APIC_LVT_TIMER 0
|
|
#define APIC_LVT_THERMAL 1
|
|
@@ -1207,6 +1225,17 @@
|
|
#define APIC_LVT_LINT0 3
|
|
#define APIC_LVT_LINT1 4
|
|
#define APIC_LVT_ERROR 5
|
|
+
|
|
+/* APIC delivery modes */
|
|
+#define APIC_DM_FIXED 0
|
|
+#define APIC_DM_LOWPRI 1
|
|
+#define APIC_DM_SMI 2
|
|
+/* RESERVED 3 */
|
|
+#define APIC_DM_NMI 4
|
|
+#define APIC_DM_INIT 5
|
|
+#define APIC_DM_SIPI 6
|
|
+#define APIC_DM_EXTINT 7
|
|
+
|
|
Bit32u timer_initial, timer_current, timer_divconf;
|
|
Boolean timer_active; // internal state, not accessible from bus
|
|
Bit32u timer_divide_counter, timer_divide_factor;
|
|
@@ -1234,8 +1263,8 @@
|
|
// on local APIC, trigger means raise the CPU's INTR line. For now
|
|
// I also have to raise pc_system.INTR but that should be replaced
|
|
// with the cpu-specific INTR signals.
|
|
- virtual void trigger_irq (unsigned num, unsigned from);
|
|
- virtual void untrigger_irq (unsigned num, unsigned from);
|
|
+ virtual void trigger_irq (unsigned num, unsigned from, unsigned trigger_mode);
|
|
+ virtual void untrigger_irq (unsigned num, unsigned from, unsigned trigger_mode);
|
|
Bit8u acknowledge_int (); // only the local CPU should call this
|
|
int highest_priority_int (Bit8u *array);
|
|
void service_local_apic ();
|
|
@@ -1247,14 +1276,20 @@
|
|
virtual Boolean deliver (Bit8u destination, Bit8u dest_mode, Bit8u delivery_mode, Bit8u vector, Bit8u polarity, Bit8u trig_mode);
|
|
Bit8u get_ppr ();
|
|
Bit8u get_apr ();
|
|
+ Bit8u get_apr_lowpri();
|
|
+ Boolean is_focus(Bit32u vector);
|
|
+ Boolean bypass_irr_isr;
|
|
+ void adjust_arb_id(int winning_id); // adjust the arbitration id after a bus arbitration
|
|
void periodic (Bit32u usec_delta);
|
|
void set_divide_configuration (Bit32u value);
|
|
virtual void update_msr_apicbase(Bit32u newaddr);
|
|
virtual void set_arb_id (int newid);
|
|
};
|
|
|
|
-#define APIC_MAX_ID 16
|
|
+#define APIC_MAX_ID 0xff
|
|
+#define APIC_ID_MASK 0xff
|
|
extern bx_generic_apic_c *apic_index[APIC_MAX_ID];
|
|
+extern bx_local_apic_c *local_apic_index[BX_LOCAL_APIC_NUM];
|
|
#endif // if BX_SUPPORT_APIC
|
|
|
|
|
|
@@ -2603,6 +2638,7 @@
|
|
#if BX_SUPPORT_APIC
|
|
bx_local_apic_c local_apic;
|
|
Boolean int_from_local_apic;
|
|
+ static Bit32u cpu_online_map;
|
|
#endif
|
|
};
|
|
|
|
Index: iodev/ioapic.cc
|
|
===================================================================
|
|
RCS file: /cvsroot/bochs/bochs/iodev/ioapic.cc,v
|
|
retrieving revision 1.9
|
|
diff -u -r1.9 ioapic.cc
|
|
--- iodev/ioapic.cc 29 Aug 2002 16:52:47 -0000 1.9
|
|
+++ iodev/ioapic.cc 25 Sep 2002 14:41:10 -0000
|
|
@@ -11,16 +11,22 @@
|
|
void
|
|
bx_io_redirect_entry_t::parse_value ()
|
|
{
|
|
- dest = (value >> 56) & 0xff;
|
|
+ dest = (value >> 56) & APIC_ID_MASK;
|
|
masked = (value >> 16) & 1;
|
|
trig_mode = (value >> 15) & 1;
|
|
remote_irr = (value >> 14) & 1;
|
|
polarity = (value >> 13) & 1;
|
|
//delivery_status = (value >> 12) & 1;
|
|
- delivery_status = 0; // always say the message has gone through
|
|
+ delivery_status = 0; // we'll change this later...
|
|
dest_mode = (value >> 11) & 1;
|
|
+#if 0
|
|
+ if (dest_mode == 1)
|
|
+ dest = (value >> 56) & APIC_ID_MAX; // processor mask
|
|
+ else
|
|
+ dest = (value >> 56) & APIC_ID_MASK; // APIC ID
|
|
+#endif
|
|
delivery_mode = (value >> 8) & 7;
|
|
- vector = (value >> 0) & 0xff;
|
|
+ vector = value & 0xff;
|
|
}
|
|
|
|
void
|
|
@@ -45,6 +51,7 @@
|
|
bx_generic_apic_c::init ();
|
|
BX_DEBUG(("initializing I/O APIC"));
|
|
base_addr = 0xfec00000;
|
|
+ set_id(BX_IOAPIC_DEFAULT_ID);
|
|
ioregsel = 0;
|
|
// all interrupts masked
|
|
for (int i=0; i<BX_IOAPIC_NUM_PINS; i++) {
|
|
@@ -74,15 +81,14 @@
|
|
}
|
|
// only reached when reading data register
|
|
switch (ioregsel) {
|
|
- case 0x00: // APIC ID
|
|
- *data = ((id & 0xf) << 24);
|
|
+ case 0x00: // APIC ID, note this is 4bits, the upper 4 are reserved
|
|
+ *data = ((id & APIC_ID_MASK) << 24);
|
|
return;
|
|
case 0x01: // version
|
|
*data = (((BX_IOAPIC_NUM_PINS-1) & 0xff) << 16)
|
|
| (BX_IOAPIC_VERSION_ID & 0x0f);
|
|
return;
|
|
case 0x02:
|
|
- BX_INFO(("IOAPIC: arbitration ID unsupported, returned 0"));
|
|
*data = 0;
|
|
return;
|
|
default:
|
|
@@ -111,7 +117,7 @@
|
|
switch (ioregsel) {
|
|
case 0x00: // set APIC ID
|
|
{
|
|
- Bit8u newid = (*value >> 24) & 0xf;
|
|
+ Bit8u newid = (*value >> 24) & APIC_ID_MASK;
|
|
BX_INFO(("IOAPIC: setting id to 0x%x", newid));
|
|
set_id (newid);
|
|
return;
|
|
@@ -140,14 +146,16 @@
|
|
|
|
void bx_ioapic_c::trigger_irq (unsigned vector, unsigned from)
|
|
{
|
|
- BX_DEBUG(("IOAPIC: received interrupt %d", vector));
|
|
- if (vector >= 0 && vector < BX_IOAPIC_NUM_PINS) {
|
|
+ BX_DEBUG(("IOAPIC: received vector %d", vector));
|
|
+ if ((vector >= 0) && (vector <= BX_APIC_LAST_VECTOR)) {
|
|
Bit32u bit = 1<<vector;
|
|
if ((irr & bit) == 0) {
|
|
irr |= bit;
|
|
service_ioapic ();
|
|
}
|
|
- } else BX_PANIC(("IOAPIC: vector %d out of range", vector));
|
|
+ } else {
|
|
+ BX_PANIC(("IOAPIC: vector %#x out of range", vector));
|
|
+ }
|
|
}
|
|
|
|
void bx_ioapic_c::untrigger_irq (unsigned num, unsigned from)
|
|
@@ -157,16 +165,34 @@
|
|
|
|
void bx_ioapic_c::service_ioapic ()
|
|
{
|
|
+ static unsigned int stuck = 0;
|
|
// look in IRR and deliver any interrupts that are not masked.
|
|
BX_DEBUG(("IOAPIC: servicing"));
|
|
+
|
|
for (unsigned bit=0; bit < BX_IOAPIC_NUM_PINS; bit++) {
|
|
if (irr & (1<<bit)) {
|
|
bx_io_redirect_entry_t *entry = ioredtbl + bit;
|
|
+ entry->parse_value();
|
|
if (!entry->masked) {
|
|
- // clear irr bit and deliver
|
|
- Boolean done = deliver (entry->dest, entry->dest_mode, entry->delivery_mode, entry->vector, entry->polarity, entry->trig_mode);
|
|
- if (done) irr &= ~(1<<bit);
|
|
+ Boolean done;
|
|
+ // deliver and clear irr bit
|
|
+ done = deliver (entry->dest, entry->dest_mode,
|
|
+ entry->delivery_mode, entry->vector,
|
|
+ entry->polarity, entry->trig_mode);
|
|
+
|
|
+ if (done) {
|
|
+ irr &= ~(1<<bit);
|
|
+ entry->delivery_status = 0;
|
|
+ stuck = 0;
|
|
+ } else {
|
|
+ entry->delivery_status = 1;
|
|
+ stuck++;
|
|
+ if (stuck > 5)
|
|
+ BX_INFO(("vector %#x stuck?\n", entry->vector));
|
|
+ }
|
|
+
|
|
}
|
|
}
|
|
}
|
|
}
|
|
+
|