---------------------------------------------------------------------- Patch name: patch.apic-mrieker Author: mrieker Date: 27 June 2002 1st 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 2nd detailed description: more complete apic stuff fixed a couple things: - it works on win32 vc 6. vc 6 apparently doesn't zero mem on a 'new bx_cpu_c' and apic init functions depended on that - don't consider apic on a cpu that hasn't been started yet for a destination bitmask bit - fix some compile errors in vc 6 complaining about 'int bit=0' - implement reading IRR,TMR,ISR registers Patch was created with: cvs diff -u Apply patch to what version: cvs checked out on 27 June 2002 Instructions: To patch, go to main bochs directory. Type "patch -p0 < THIS_PATCH_FILE". ---------------------------------------------------------------------- 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 27 Jun 2002 19:36:17 -0000 @@ -6,6 +6,9 @@ #include "bochs.h" #include +#ifdef LOG_THIS +#undef LOG_THIS +#endif #define LOG_THIS this-> bx_generic_apic_c *apic_index[APIC_MAX_ID]; @@ -169,32 +178,51 @@ bx_bool 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; bitget_ppr () < lowest_priority) { - lowest_priority = apic->get_ppr (); lowest_mask = 1< cpu -> eflags.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 @@ -210,32 +238,51 @@ } return true; case 6: // Start Up (local apic only) BX_ASSERT (get_type () == APIC_TYPE_LOCAL_APIC); for (int bit=0; bitstartup_msg (vector); return true; + case 4: // NMI + { + BX_INFO (("APIC NMI deliver_bitmask %X", deliver_bitmask)); + for (int bit=0; bit get_cpu (); // if so, point to its CPU struct + if (itscpu -> eflags.if_ || !(itscpu -> debug_trap & 0x80000000)) { // make sure it's apic has been IPI'd + BX_INFO (("APIC NMI bit %u, cpu %s", bit, itscpu -> name)); // ok, NMI its ass + 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)); } @@ -269,9 +316,9 @@ bx_local_apic_c::bx_local_apic_c(BX_CPU_C *mycpu) : bx_generic_apic_c () { - char buffer[16]; cpu = mycpu; - hwreset (); + // hwreset (); moved to be called after the 'new' - as on vc++, the struct doesn't + // get zeroed so we end up outputting a page of garbage characters } void @@ -369,16 +415,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) @@ -568,30 +628,38 @@ int bx_local_apic_c::highest_priority_int (Bit8u *array) { - for (int i=0; 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; @@ -617,20 +686,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) 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 +722,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; vecname)); Index: cpu/cpu.cc =================================================================== RCS file: /cvsroot/bochs/bochs/cpu/cpu.cc,v retrieving revision 1.32 diff -u -r1.32 cpu.cc --- cpu/cpu.cc 6 Jun 2002 23:03:09 -0000 1.32 +++ cpu/cpu.cc 27 Jun 2002 19:36:18 -0000 @@ -539,7 +539,7 @@ #else while (1) { #endif - if (BX_CPU_INTR && BX_CPU_THIS_PTR get_IF ()) { + if ((BX_CPU_INTR && BX_CPU_THIS_PTR get_IF()) || BX_CPU_THIS_PTR nmi_queued) { break; } BX_TICK1(); @@ -549,7 +549,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_INTR && BX_CPU_THIS_PTR get_IF ()) { + if ((BX_CPU_INTR && BX_CPU_THIS_PTR get_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 @@ -610,6 +611,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, EIP); + } + else if (BX_CPU_THIS_PTR INTR && BX_CPU_THIS_PTR get_IF() && BX_DBG_ASYNC_INTR) { Bit8u vector; Index: cpu/cpu.h =================================================================== RCS file: /cvsroot/bochs/bochs/cpu/cpu.h,v retrieving revision 1.22 diff -u -r1.22 cpu.h --- cpu/cpu.h 5 Jun 2002 21:51:30 -0000 1.22 +++ cpu/cpu.h 27 Jun 2002 19:36:19 -0000 @@ -820,6 +820,7 @@ volatile bx_bool async_event; volatile bx_bool INTR; volatile bx_bool kill_bochs_request; + volatile bx_bool nmi_queued; /* wether this CPU is the BSP always set for UP */ bx_bool bsp; Index: cpu/init.cc =================================================================== RCS file: /cvsroot/bochs/bochs/cpu/init.cc,v retrieving revision 1.16 diff -u -r1.16 init.cc --- cpu/init.cc 5 Jun 2002 21:51:30 -0000 1.16 +++ cpu/init.cc 27 Jun 2002 19:36:19 -0000 @@ -603,6 +603,10 @@ dynamic_init(); #endif + // No NMI queued to the processor + + BX_CPU_THIS_PTR nmi_queued = 0; + #if (BX_SMP_PROCESSORS > 1) // notice if I'm the bootstrap processor. If not, do the equivalent of // a HALT instruction. @@ -620,6 +624,8 @@ BX_INFO(("CPU[%d] is an application processor. Halting until IPI.", apic_id)); debug_trap |= 0x80000000; async_event = 1; + BX_CPU_THIS_PTR clear_IF(); // apic code uses this to distinguish from a normal HLT instruction + // ... normal HLT's should not be executed with interrupts inhibited } #endif }