---------------------------------------------------------------------- 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]; @@ -15,7 +18,7 @@ id = APIC_UNKNOWN_ID; put("APIC?"); settype(APICLOG); - hwreset (); + // hwreset (); see bx_local_apic_c::bx_local_apic_c } bx_generic_apic_c::~bx_generic_apic_c () @@ -115,6 +118,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")); @@ -155,7 +164,7 @@ mask = 0xff; } else BX_PANIC(("bx_generic_apic_c::deliver: illegal physical destination %02x", dest)); } else { - // logical destination. call match_logical_addr for each APIC. + // logical destination. call match_logical_addr for each local APIC. if (dest == 0) return 0; for (int i=0; imatch_logical_addr(dest)) @@ -169,32 +178,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; 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; + { + 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)); } + // Fixed delivery mode + if (bx_dbg.apic) BX_INFO(("delivering vector=0x%02x to bitmask=%04x", (int)vector, deliver_bitmask)); + for (int bit=0; bittrigger_irq (vector, id); } } } + return true; } @@ -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 @@ -312,9 +359,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 +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) @@ -407,6 +456,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 +464,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 +539,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)); } @@ -498,6 +547,10 @@ void bx_local_apic_c::read_aligned (Bit32u addr, Bit32u *data, unsigned len) { + Bit8u *bitarray; + Bit32u bitlong; + int i; + assert (len == 4); *data = 0; // default value for unimplemented registers Bit32u addr2 = addr & 0xff0; @@ -532,8 +585,15 @@ 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")); + if (addr2 & 0x200) bitarray = irr; // 200-27F: irr + else if (addr2 & 0x80) bitarray = tmr; // 180-1FF: tmr + else bitarray = isr; // 100-17F: isr + bitarray += ((addr2 << 1) & 0xE0) + 32; // point just past desired group of 32 bits/bytes + for (i = 32; -- i >= 0;) { // loop through 32 times + bitlong *= 2; // make room for new bit + bitlong |= *(-- bitarray); // or in the bit (assumes irr,tmr,isr elements are only 0 or 1) + } + *data = bitlong; break; case 0x280: // error status reg *data = err_status; break; @@ -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; @@ -599,9 +667,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 +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) + 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)); @@ -667,18 +750,20 @@ Bit32u bx_local_apic_c::get_delivery_bitmask (Bit8u dest, Bit8u dest_mode) { - int dest_shorthand = (icr_low >> 18) & 3; - Bit32u all_mask = (1<> 18) & 3) { 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<= (ppr & 0xF0))) ppr = task_priority; // or if tpr is higher, use that + else ppr &= 0xF0; + return (ppr); } 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 @@ -154,7 +154,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) { @@ -539,7 +539,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(); @@ -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_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 @@ -603,6 +603,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. @@ -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, 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.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 @@ -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/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; + eflags.if_ = 0; // apic code uses this to distinguish from a normal HLT instruction + // ... normal HLT's should not be executed with interrupts inhibited } #endif }