///////////////////////////////////////////////////////////////////////// // $Id: apic.cc,v 1.28.2.1 2003/01/01 22:33:30 cbothamy Exp $ ///////////////////////////////////////////////////////////////////////// // #define NEED_CPU_REG_SHORTCUTS 1 #include "bochs.h" #if BX_SUPPORT_APIC #define LOG_THIS this-> bx_generic_apic_c *apic_index[APIC_MAX_ID]; bx_generic_apic_c::bx_generic_apic_c () { id = APIC_UNKNOWN_ID; put("APIC?"); settype(APICLOG); hwreset (); } bx_generic_apic_c::~bx_generic_apic_c () { } void bx_generic_apic_c::set_arb_id (int new_arb_id) { // politely ignore it. This gets sent to every APIC, regardless of its // type. } // init is called during RESET and when an INIT message is delivered. void bx_generic_apic_c::init () { } void bx_local_apic_c::update_msr_apicbase(Bit32u newbase) { Bit64u val64; val64 = newbase << 12; /* push the APIC base address to bits 12:35 */ val64 += cpu->msr.apicbase & 0x0900; /* don't modify other apicbase or reserved bits */ cpu->msr.apicbase = val64; } void bx_generic_apic_c::set_base (Bit32u newbase) { BX_INFO(("relocate APIC id=%d to %8x", id, newbase)); base_addr = newbase; } void bx_generic_apic_c::set_id (Bit8u newid) { // update apic_index if (id != APIC_UNKNOWN_ID) { BX_ASSERT (id < APIC_MAX_ID); if (apic_index[id] != this) BX_PANIC(("inconsistent APIC id table")); apic_index[id] = NULL; } id = newid; if (id != APIC_UNKNOWN_ID) { if (apic_index[id] != NULL) BX_PANIC(("duplicate APIC id assigned")); apic_index[id] = this; } } void bx_generic_apic_c::reset_all_ids () { for (int i=0; iset_id (APIC_UNKNOWN_ID); } } char * bx_generic_apic_c::get_name () { BX_PANIC(("get_name called on bx_generic_apic_c base class")); return NULL; } bx_bool bx_generic_apic_c::is_selected (Bit32u addr, Bit32u len) { if ((addr & ~0xfff) == get_base ()) { if ((addr & 0xf != 0) || (len != 4)) BX_INFO(("warning: misaligned or wrong-size APIC write. addr=%08x, len=%d", addr, len)); return true; } return false; } void bx_generic_apic_c::read (Bit32u addr, void *data, unsigned len) { if ((addr & ~0xf) != ((addr+len-1) & ~0xf)) BX_PANIC(("APIC read spans 32-bit boundary")); Bit32u value; read_aligned (addr, &value, 4); if ((addr&3) == 0) { *((Bit32u *)data) = value; return; } // handle partial word read, independent of endian-ness. Bit8u bytes[4]; bytes[0] = value & 0xff; bytes[1] = (value >> 8) & 0xff; bytes[2] = (value >> 16) & 0xff; bytes[3] = (value >> 24) & 0xff; Bit8u *p1 = bytes+(addr&3); Bit8u *p2 = (Bit8u *)data; for (unsigned i=0; imatch_logical_addr(dest)) mask |= (1<get_ppr () < lowest_priority) { lowest_priority = apic->get_ppr (); lowest_mask = 1<= 0); } 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; bitinit (); } } return true; case 6: // Start Up (local apic only) BX_ASSERT (get_type () == APIC_TYPE_LOCAL_APIC); for (bit=0; bitstartup_msg (vector); return true; case 7: // ExtINT we can deliver this as fixed. In theory this requires // extra work from a connected PIC such as an 8259A but we can // skip all of that since all our pins are wired to the IOAPIC break; case 2: // SMI case 3: // reserved case 4: // NMI 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 (bit=0; bittrigger_irq (vector, id); } } } return true; } bx_bool 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) { int trig_mode = (icr_low >> 15) & 1; int level = (icr_low >> 14) & 1; if (level == 0 && trig_mode == 1) { // special mode in local apic. See "INIT Level Deassert" in the // Intel Soft. Devel. Guide Vol 3, page 7-34. This magic code // 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; bitset_arb_id (apic_index[bit]->get_id ()); } return true; } } // 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); } bx_local_apic_c::bx_local_apic_c(BX_CPU_C *mycpu, int cpuno) : bx_generic_apic_c () { cpu = mycpu; sprintf (cpu -> name, "%d", cpuno); hwreset (); INTR = 0; } void bx_local_apic_c::set_arb_id (int new_arb_id) { BX_DEBUG (("set arbitration ID to %d", new_arb_id)); arb_id = new_arb_id; } void bx_local_apic_c::hwreset () { /* same as INIT but also sets arbitration ID and APIC ID */ init (); /* since id is set explicitly by the function that creates the CPU object, do not mess around with it */ // id = APIC_UNKNOWN_ID; arb_id = id; } void bx_local_apic_c::init () { bx_generic_apic_c::init (); BX_INFO(("local apic in %s initializing", cpu->name)); // default address for a local APIC, can be moved base_addr = APIC_BASE_ADDR; update_msr_apicbase(base_addr); err_status = 0; log_dest = 0; dest_format = 0xf; for (int bit=0; bitlocal_apic.periodic_smf, 0, 0, 0, "lapic"); } BX_CPU_C *bx_local_apic_c::get_cpu (Bit8u id) { BX_ASSERT (id < APIC_MAX_ID); return cpu; } bx_local_apic_c::~bx_local_apic_c(void) { // nothing for now } void bx_local_apic_c::set_id (Bit8u newid) { bx_generic_apic_c::set_id (newid); sprintf (cpu->name, "CPU apicid=%02x", (Bit32u)id); if (id <= 15) { char buffer[16]; sprintf (buffer, "APIC%x", id); put(buffer); settype(CPU0LOG + id); sprintf (buffer, "CPU%x", id); cpu->put (buffer); } else { BX_INFO (("naming convention for apics requires id=0-15 only")); } if(BX_CPU_LEVEL<2) BX_INFO(( "8086" )); else BX_INFO(( "80%d86", BX_CPU_LEVEL )); } char * bx_local_apic_c::get_name() { return cpu->name; } void bx_local_apic_c::set_divide_configuration (Bit32u value) { BX_ASSERT (value == (value & 0x0b)); // move bit 3 down to bit 0. value = ((value & 8) >> 1) | (value & 3); BX_ASSERT (value >= 0 && value <= 7); timer_divide_factor = (value==7)? 1 : (2 << value); BX_DEBUG(("%s: set timer divide factor to %d", cpu->name, timer_divide_factor)); } void bx_local_apic_c::write (Bit32u addr, Bit32u *data, unsigned len) { if (len != 4) { BX_PANIC (("local apic write with len=%d (should be 4)", len)); } BX_DEBUG(("%s: write %08x to APIC address %08x", cpu->name, *data, addr)); addr &= 0xff0; switch (addr) { case 0x20: // local APIC id id = ((*data)>>24) & 0xf; break; case 0x80: // task priority { Bit8u oldtskpri; oldtskpri = task_priority; task_priority = *data & 0xff; if (task_priority < oldtskpri) service_local_apic (); 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")); } else { BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu->name, vec)); isr[vec] = 0; service_local_apic (); } if (bx_dbg.apic) print_status (); } break; case 0xd0: // logical destination log_dest = (*data >> 24) & 0xff; BX_DEBUG (("set logical destiation to %02x", log_dest)); break; case 0xe0: // destination format dest_format = (*data >> 28) & 0xf; BX_DEBUG (("set destination format to %02x", dest_format)); break; case 0xf0: // spurious interrupt vector spurious_vec = (spurious_vec & 0x0f) | (*data & 0x3f0); break; case 0x280: // error status reg // Here's what the IA-devguide-3 says on p.7-45: // The ESR is a read/write register and is reset after being written to // by the processor. A write to the ESR must be done just prior to // reading the ESR to allow the register to be updated. // This doesn't seem clear. If the write clears the register, then // wouldn't you always read zero? Otherwise, what does the write do? err_status = 0; break; case 0x300: // interrupt command reg 0-31 { icr_low = *data & ~(1<<12); // force delivery status bit = 0 (idle) int dest = (icr_high >> 24) & 0xff; int trig_mode = (icr_low >> 15) & 1; int level = (icr_low >> 14) & 1; 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. bx_bool 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 icr_high = *data & 0xff000000; break; case 0x320: // LVT Timer Reg lvt[APIC_LVT_TIMER] = *data & 0x310ff; break; case 0x330: // LVT Thermal Monitor lvt[APIC_LVT_THERMAL] = *data & 0x117ff; break; case 0x340: // LVT Performance Counter lvt[APIC_LVT_PERFORM] = *data & 0x117ff; break; case 0x350: // LVT LINT0 Reg lvt[APIC_LVT_LINT0] = *data & 0x1f7ff; break; case 0x360: // LVT Lint1 Reg lvt[APIC_LVT_LINT1] = *data & 0x1f7ff; break; case 0x370: // LVT Error Reg lvt[APIC_LVT_ERROR] = *data & 0x117ff; break; case 0x380: // initial count for timer { // If active before, deactive the current timer before changing it. if (timer_active) bx_pc_system.deactivate_timer(timer_handle); timer_initial = *data; if (timer_initial == 0) BX_PANIC(("APIC: W(init timer count): count=0")); // This should trigger the counter to start. If already started, // restart from the new start value. //fprintf(stderr, "APIC: W(Initial Count Register) = %u\n", *data); timer_current = timer_initial; timer_active = true; //timer_divide_counter = 0; // KPL: delete this field. Bit32u timervec = lvt[APIC_LVT_TIMER]; bx_bool continuous = (timervec & 0x20000) > 0; ticksInitial = bx_pc_system.getTicksTotal(); // Take a reading. bx_pc_system.activate_timer_ticks(timer_handle, Bit64u(timer_initial) * Bit64u(timer_divide_factor), continuous); } break; case 0x3e0: // timer divide configuration // only bits 3, 1, and 0 are writable timer_divconf = *data & 0xb; set_divide_configuration (timer_divconf); break; /* all read-only registers go here */ case 0x30: // local APIC version case 0x90: // arbitration priority case 0xa0: // processor priority // ISRs not writable case 0x100: case 0x110: case 0x120: case 0x130: case 0x140: case 0x150: case 0x160: case 0x170: // TMRs not writable case 0x180: case 0x190: case 0x1a0: case 0x1b0: case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0: // IRRs not writable case 0x200: case 0x210: case 0x220: case 0x230: case 0x240: case 0x250: case 0x260: case 0x270: // current count for timer case 0x390: // all read-only registers should fall into this line BX_INFO(("warning: write to read-only APIC register 0x%02x", addr)); break; default: err_status |= APIC_ERR_ILLEGAL_ADDR; // but for now I want to know about it in case I missed some. BX_PANIC(("APIC register %08x not implemented", addr)); } } void bx_local_apic_c::startup_msg (Bit32u vector) { if (cpu->debug_trap & 0x80000000) { cpu->debug_trap &= ~0x80000000; cpu->dword.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->dword.eip)); } else { BX_INFO(("%s started up by APIC, but was not halted at the time", cpu->name)); } } void bx_local_apic_c::read_aligned (Bit32u addr, Bit32u *data, unsigned len) { if (len != 4) { BX_PANIC (("local apic write with len=%d (should be 4)", len)); } *data = 0; // default value for unimplemented registers Bit32u addr2 = addr & 0xff0; switch (addr2) { case 0x20: // local APIC id *data = (id) << 24; break; case 0x30: // local APIC version *data = 0x00170011; break; case 0x80: // task priority *data = task_priority & 0xff; break; case 0x90: // arbitration priority *data = get_apr (); break; case 0xa0: // processor priority *data = get_ppr (); break; case 0xb0: // EOI /* * Read-modify-write operations should operate without generating * exceptions, and are used by some operating systems to EOI. * The results of reads should be ignored by the OS. */ break; case 0xd0: // logical destination *data = (log_dest & 0xff) << 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: // read ISR case 0x140: case 0x150: case 0x160: case 0x170: case 0x180: case 0x190: case 0x1a0: case 0x1b0: // read TMR case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0: case 0x200: case 0x210: case 0x220: case 0x230: // read IRR case 0x240: case 0x250: case 0x260: case 0x270: { int i; Bit8u *p; Bit32u val; p = NULL; if ((addr2 & 0xF80) == 0x100) p = isr; if ((addr2 & 0xF80) == 0x180) p = tmr; if ((addr2 & 0xF80) == 0x200) p = irr; p += (addr2 << 1) & 0xE0; val = 0; for (i = 32; -- i >= 0;) { val *= 2; val += p[i] & 1; } *data = val; break; } case 0x280: // error status reg *data = err_status; break; case 0x300: // interrupt command reg 0-31 *data = icr_low; break; case 0x310: // interrupt command reg 31-63 *data = icr_high; break; case 0x320: // LVT Timer Reg case 0x330: // LVT Thermal Monitor case 0x340: // LVT Performance Counter case 0x350: // LVT LINT0 Reg case 0x360: // LVT Lint1 Reg case 0x370: // LVT Error Reg { int index = (addr2 - 0x320) >> 4; *data = lvt[index]; break; } case 0x380: // initial count for timer *data = timer_initial; //fprintf(stderr, "APIC: R(Initial Count Register) = %u\n", *data); break; case 0x390: // current count for timer { if (timer_active==0) *data = timer_current; else { Bit64u delta64; Bit32u delta32; delta64 = (bx_pc_system.time_ticks() - ticksInitial) / timer_divide_factor; delta32 = (Bit32u) delta64; if (delta32 > timer_initial) BX_PANIC(("APIC: R(curr timer count): delta < initial")); timer_current = timer_initial - delta32; *data = timer_current; } //fprintf(stderr, "APIC: R(Current Count Register) = %u\n", *data); } break; case 0x3e0: // timer divide configuration *data = timer_divconf; break; default: BX_INFO(("APIC register %08x not implemented", addr)); } if (bx_dbg.apic) BX_INFO(("%s: read from APIC address %08x = %08x", cpu->name, addr, *data)); } int bx_local_apic_c::highest_priority_int (Bit8u *array) { int i; for (i = BX_LOCAL_APIC_MAX_INTS; -- i >= 0;) if (array[i]) break; return (i); } void bx_local_apic_c::service_local_apic () { if (bx_dbg.apic) { BX_INFO(("service_local_apic()")); print_status (); } if (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 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)); return; } if ((first_irr & 0xF0) <= (task_priority & 0xF0)) { if (bx_dbg.apic) { BX_INFO (("local apic (%s): not delivering int %X because task_priority is %X", cpu -> name, first_irr, task_priority)); } 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)); INTR = 1; cpu->async_event = 1; } void bx_local_apic_c::trigger_irq (unsigned vector, unsigned from) { irr[vector] = 1; service_local_apic (); } void bx_local_apic_c::untrigger_irq (unsigned vector, unsigned from) { 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 (); } Bit8u bx_local_apic_c::acknowledge_int () { // CPU calls this when it is ready to service one interrupt if (!INTR) BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu->name)); BX_ASSERT (INTR); int vector = highest_priority_int (irr); if (vector < 0) goto spurious; if ((vector & 0xF0) <= get_ppr ()) goto spurious; 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 (); } INTR = 0; cpu->async_event = 1; service_local_apic (); // will set INTR again if another is ready return vector; spurious: INTR = 0; cpu -> async_event = 1; return (spurious_vec); } void bx_local_apic_c::print_status () { BX_INFO(("%s local apic: status is {:", cpu->name)); for (int vec=0; vecname)); } bx_bool 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; } bx_bool 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; } Bit32u bx_local_apic_c::get_delivery_bitmask (Bit8u dest, Bit8u dest_mode) { int dest_shorthand = (icr_low >> 18) & 3; Bit32u all_mask = (1<get_type () != APIC_TYPE_LOCAL_APIC)) mask &= ~(1<= 0) { isrv &= 0xF0; if (((int)(task_priority & 0xF0)) < isrv) { // if .gt. task priority, return (isrv); // ... return highest prio int in service } } return (task_priority & 0xFF); // else, return task priority } Bit8u bx_local_apic_c::get_apr () { return arb_id; } void bx_local_apic_c::periodic_smf(void *this_ptr) { bx_local_apic_c *class_ptr = (bx_local_apic_c *) this_ptr; class_ptr->periodic(); } void bx_local_apic_c::periodic(void) // KPL: changed prototype { if (!timer_active) { BX_PANIC(("%s: bx_local_apic_c::periodic called, timer_active==0", cpu->name)); return; } BX_DEBUG(("%s: bx_local_apic_c::periodic called", cpu->name)); // timer reached zero since the last call to periodic. Bit32u timervec = lvt[APIC_LVT_TIMER]; if (timervec & 0x20000) { // Periodic mode. // If timer is not masked, trigger interrupt. if ( (timervec & 0x10000)==0 ) trigger_irq (timervec & 0xff, id); // Reload timer values. timer_current = timer_initial; ticksInitial = bx_pc_system.getTicksTotal(); // Take a reading. 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 is not masked, trigger interrupt. if ( (timervec & 0x10000)==0 ) trigger_irq (timervec & 0xff, id); timer_active = false; BX_DEBUG (("%s: local apic timer (one-shot) triggered int", cpu->name)); bx_pc_system.deactivate_timer(timer_handle); // Make sure. } } #endif /* if BX_SUPPORT_APIC */