- added instructions how to update the online documentation
[bochs-mirror.git] / cpu / apic.cc
blob425d16209d4cb4704e2682f590c1ae431b243bb4
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: apic.cc,v 1.112 2008/12/01 19:06:14 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (c) 2002 Zwane Mwaikambo, Stanislav Shwartsman
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Lesser General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // Lesser General Public License for more details.
17 // You should have received a copy of the GNU Lesser General Public
18 // License along with this library; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 /////////////////////////////////////////////////////////////////////////
23 #define NEED_CPU_REG_SHORTCUTS 1
24 #include "bochs.h"
25 #include "cpu.h"
26 #include "iodev/iodev.h"
28 #if BX_SUPPORT_APIC
30 #define LOG_THIS this->
32 #define APIC_BROADCAST_PHYSICAL_DESTINATION_MODE (APIC_MAX_ID)
34 #define BX_LAPIC_FIRST_VECTOR 0x10
35 #define BX_LAPIC_LAST_VECTOR 0xfe
37 ///////////// APIC BUS /////////////
39 int apic_bus_deliver_interrupt(Bit8u vector, Bit8u dest, Bit8u delivery_mode, Bit8u dest_mode, bx_bool level, bx_bool trig_mode)
41 if(delivery_mode == APIC_DM_LOWPRI)
43 if(dest_mode == 0) {
44 // I/O subsytem initiated interrupt with lowest priority delivery
45 // mode is not supported in physical destination mode
46 // BX_ERROR(("Ignoring lowest priority interrupt in physical dest mode !"));
47 return 0;
49 else {
50 return apic_bus_deliver_lowest_priority(vector, dest, trig_mode, 0);
54 // determine destination local apics and deliver
55 if(dest_mode == 0) {
56 if(dest == APIC_BROADCAST_PHYSICAL_DESTINATION_MODE)
58 return apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, APIC_MAX_ID);
60 else {
61 // the destination is single agent
62 for (unsigned i=0;i<BX_NUM_LOCAL_APICS;i++)
64 if(BX_CPU_APIC(i)->get_id() == dest) {
65 BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
66 return 1;
70 return 0;
73 else {
74 // logical destination mode
75 if(dest == 0) return 0;
77 bx_bool interrupt_delivered = 0;
79 for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
80 if(BX_CPU_APIC(i)->match_logical_addr(dest)) {
81 BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
82 interrupt_delivered = 1;
86 return interrupt_delivered;
90 int apic_bus_deliver_lowest_priority(Bit8u vector, Bit8u dest, bx_bool trig_mode, bx_bool broadcast)
92 int i;
94 #ifndef BX_IMPLEMENT_XAPIC
95 // search for if focus processor exists
96 for (i=0; i<BX_NUM_LOCAL_APICS; i++) {
97 if(BX_CPU_APIC(i)->is_focus(vector)) {
98 BX_CPU_APIC(i)->deliver(vector, APIC_DM_LOWPRI, trig_mode);
99 return 1;
102 #endif
104 // focus processor not found, looking for lowest priority agent
105 int lowest_priority_agent = -1, lowest_priority = 0x100;
107 for (i=0; i<BX_NUM_LOCAL_APICS; i++) {
108 if(broadcast || BX_CPU_APIC(i)->match_logical_addr(dest)) {
109 #ifndef BX_IMPLEMENT_XAPIC
110 int priority = BX_CPU_APIC(i)->get_apr();
111 #else
112 int priority = BX_CPU_APIC(i)->get_tpr();
113 #endif
114 if(priority < lowest_priority) {
115 lowest_priority = priority;
116 lowest_priority_agent = i;
121 if(lowest_priority_agent >= 0)
123 BX_CPU_APIC(lowest_priority_agent)->deliver(vector, APIC_DM_LOWPRI, trig_mode);
124 return 1;
127 return 0;
130 int apic_bus_broadcast_interrupt(Bit8u vector, Bit8u delivery_mode, bx_bool trig_mode, int exclude_cpu)
132 if(delivery_mode == APIC_DM_LOWPRI)
134 return apic_bus_deliver_lowest_priority(vector, 0 /* doesn't matter */, trig_mode, 1);
137 // deliver to all bus agents except 'exclude_cpu'
138 for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
139 if(i == exclude_cpu) continue;
140 BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
143 return 1;
146 static void apic_bus_broadcast_eoi(Bit8u vector)
148 bx_devices.ioapic->receive_eoi(vector);
151 #endif
153 // available even if APIC is not compiled in
154 void apic_bus_deliver_smi(void)
156 BX_CPU(0)->deliver_SMI();
159 void apic_bus_broadcast_smi(void)
161 for (unsigned i=0; i<BX_SMP_PROCESSORS; i++)
162 BX_CPU(i)->deliver_SMI();
165 #if BX_SUPPORT_APIC
167 ////////////////////////////////////
169 bx_generic_apic_c::bx_generic_apic_c(bx_phy_address base)
171 put("APIC?");
172 settype(APICLOG);
173 id = APIC_UNKNOWN_ID;
174 set_base(base);
177 void bx_generic_apic_c::set_base(bx_phy_address newbase)
179 newbase &= (~0xfff);
180 base_addr = newbase;
181 if (id != APIC_UNKNOWN_ID)
182 BX_INFO(("relocate APIC id=%d to 0x" FMT_PHY_ADDRX, id, newbase));
185 void bx_generic_apic_c::set_id(Bit8u newid)
187 BX_INFO(("set APIC ID to %d", newid));
188 id = newid;
191 bx_bool bx_generic_apic_c::is_selected(bx_phy_address addr, unsigned len)
193 if((addr & ~0xfff) == get_base()) {
194 if((addr & 0xf) != 0)
195 BX_INFO(("warning: misaligned APIC access. addr=0x" FMT_PHY_ADDRX ", len=%d", addr, len));
196 return 1;
198 return 0;
201 void bx_generic_apic_c::read(bx_phy_address addr, void *data, unsigned len)
203 if((addr & ~0x3) != ((addr+len-1) & ~0x3)) {
204 BX_PANIC(("APIC read at address 0x" FMT_PHY_ADDRX " spans 32-bit boundary !", addr));
205 return;
207 Bit32u value;
208 read_aligned(addr & ~0x3, &value);
209 if(len == 4) { // must be 32-bit aligned
210 *((Bit32u *)data) = value;
211 return;
213 // handle partial read, independent of endian-ness
214 value >>= (addr&3)*8;
215 if (len == 1)
216 *((Bit8u *) data) = value & 0xff;
217 else if (len == 2)
218 *((Bit16u *)data) = value & 0xffff;
219 else
220 BX_PANIC(("Unsupported APIC read at address 0x" FMT_PHY_ADDRX ", len=%d", addr, len));
223 void bx_generic_apic_c::write(bx_phy_address addr, void *data, unsigned len)
225 if((addr & ~0x3) != ((addr+len-1) & ~0x3)) {
226 BX_PANIC(("APIC write at address 0x" FMT_PHY_ADDRX " spans 32-bit boundary !", addr));
227 return;
229 bx_phy_address addr_aligned = addr & ~0x3;
230 if(len == 4) { // must be 32-bit aligned
231 write_aligned(addr_aligned, (Bit32u*) data);
232 return;
234 // partial write to the apic register, need to update some bytes
235 // and do not touch the others, i.e. to do RMW operation
236 Bit32u value;
237 read_aligned(addr_aligned, &value); // apic read has no side effects
238 // handle partial write, independent of endian-ness
239 unsigned shift = (addr&3)*8;
240 if (len == 1) {
241 value &= ~(0xff << shift);
242 value |= (*((Bit8u *) data) << shift);
244 else if (len == 2) {
245 value &= ~(0xffff << shift);
246 value |= (*((Bit16u *)data) << shift);
248 else {
249 BX_PANIC(("Unsupported APIC write at address 0x" FMT_PHY_ADDRX ", len=%d", addr, len));
251 write_aligned(addr_aligned, &value);
254 bx_local_apic_c::bx_local_apic_c(BX_CPU_C *mycpu)
255 : bx_generic_apic_c(BX_LAPIC_BASE_ADDR), cpu(mycpu), cpu_id(cpu->which_cpu())
257 // KPL: Register a non-active timer for use when the timer is started.
258 timer_handle = bx_pc_system.register_timer_ticks(this,
259 BX_CPU(0)->local_apic.periodic_smf, 0, 0, 0, "lapic");
260 timer_active = 0;
262 reset(BX_RESET_HARDWARE);
264 INTR = 0;
267 void bx_local_apic_c::reset(unsigned type)
269 UNUSED(type);
271 /* same as INIT but also sets arbitration ID and APIC ID */
272 init();
275 void bx_local_apic_c::init()
277 int i;
279 bx_generic_apic_c::init();
281 BX_INFO(("local apic in %s initializing",
282 (cpu && cpu->name) ? cpu->name : "?"));
284 // default address for a local APIC, can be moved
285 base_addr = BX_LAPIC_BASE_ADDR;
286 error_status = shadow_error_status = 0;
287 log_dest = 0;
288 dest_format = 0xf;
289 icr_hi = 0;
290 icr_lo = 0;
291 task_priority = 0;
293 for(i=0; i<BX_LAPIC_MAX_INTS; i++) {
294 irr[i] = isr[i] = tmr[i] = 0;
297 timer_divconf = 0;
298 timer_divide_factor = 1;
299 timer_initial = 0;
300 timer_current = 0;
302 if(timer_active) {
303 bx_pc_system.deactivate_timer(timer_handle);
304 timer_active = 0;
307 for(i=0; i<APIC_LVT_ENTRIES; i++) {
308 lvt[i] = 0x10000; // all LVT are masked
311 spurious_vector = 0xff; // software disabled(bit 8)
312 software_enabled = 0;
313 focus_disable = 0;
316 void bx_local_apic_c::set_id(Bit8u newid)
318 bx_generic_apic_c::set_id(newid);
319 sprintf(cpu->name, "CPU apicid=%02x",(Bit32u)id);
320 if(id < APIC_MAX_ID) {
321 char buffer[16];
322 sprintf(buffer, "APIC%x", id);
323 put(buffer);
324 settype(CPU0LOG + id);
325 sprintf(buffer, "CPU%x", id);
326 cpu->put(buffer);
327 } else {
328 BX_INFO(("naming convention for apics requires id=0-%d only", APIC_MAX_ID));
330 if(BX_CPU_LEVEL<2)
331 BX_INFO(("8086"));
332 else
333 BX_INFO(("80%d86", BX_CPU_LEVEL));
336 // APIC write: 4 byte write to 16-byte aligned APIC address
337 void bx_local_apic_c::write_aligned(bx_phy_address addr, Bit32u *data)
339 BX_DEBUG(("%s: LAPIC write 0x" FMT_PHY_ADDRX " to address %08x", cpu->name, *data, addr));
340 BX_ASSERT((addr & 0xf) == 0);
341 Bit32u apic_reg = addr & 0xff0;
342 Bit32u value = *data;
343 switch(apic_reg) {
344 case 0x20: // local APIC id
345 id = (value>>24) & APIC_ID_MASK;
346 break;
347 case 0x80: // task priority
348 set_tpr(value & 0xff);
349 break;
350 case 0xb0: // EOI
351 receive_EOI(value);
352 break;
353 case 0xd0: // logical destination
354 log_dest = (value >> 24) & APIC_ID_MASK;
355 BX_DEBUG(("set logical destination to %02x", log_dest));
356 break;
357 case 0xe0: // destination format
358 dest_format = (value >> 28) & 0xf;
359 BX_DEBUG(("set destination format to %02x", dest_format));
360 break;
361 case 0xf0: // spurious interrupt vector
362 write_spurious_interrupt_register(value);
363 break;
364 case 0x280: // error status reg
365 // Here's what the IA-devguide-3 says on p.7-45:
366 // The ESR is a read/write register and is reset after being written to
367 // by the processor. A write to the ESR must be done just prior to
368 // reading the ESR to allow the register to be updated.
369 error_status = shadow_error_status;
370 shadow_error_status = 0;
371 break;
372 case 0x300: // interrupt command reg 0-31
373 icr_lo = value & ~(1<<12); // force delivery status bit = 0(idle)
374 send_ipi();
375 break;
376 case 0x310: // interrupt command reg 31-63
377 icr_hi = value & 0xff000000;
378 break;
379 case 0x320: // LVT Timer Reg
380 lvt[APIC_LVT_TIMER] = value & 0x300ff;
381 if(! software_enabled) lvt[APIC_LVT_TIMER] |= 0x10000;
382 break;
383 case 0x330: // LVT Thermal Monitor
384 lvt[APIC_LVT_THERMAL] = value & 0x107ff;
385 if(! software_enabled) lvt[APIC_LVT_THERMAL] |= 0x10000;
386 break;
387 case 0x340: // LVT Performance Counter
388 lvt[APIC_LVT_PERFORM] = value & 0x107ff;
389 if(! software_enabled) lvt[APIC_LVT_PERFORM] |= 0x10000;
390 break;
391 case 0x350: // LVT LINT0 Reg
392 lvt[APIC_LVT_LINT0] = value & 0x1a7ff;
393 if(! software_enabled) lvt[APIC_LVT_LINT0] |= 0x10000;
394 break;
395 case 0x360: // LVT Lint1 Reg
396 lvt[APIC_LVT_LINT1] = value & 0x1a7ff;
397 if(! software_enabled) lvt[APIC_LVT_LINT1] |= 0x10000;
398 break;
399 case 0x370: // LVT Error Reg
400 lvt[APIC_LVT_ERROR] = value & 0x100ff;
401 if(! software_enabled) lvt[APIC_LVT_ERROR] |= 0x10000;
402 break;
403 case 0x380: // initial count for timer
404 set_initial_timer_count(value);
405 break;
406 case 0x3e0: // timer divide configuration
407 // only bits 3, 1, and 0 are writable
408 timer_divconf = value & 0xb;
409 set_divide_configuration(timer_divconf);
410 break;
411 /* all read-only registers go here */
412 case 0x30: // local APIC version
413 case 0x90: // arbitration priority
414 case 0xa0: // processor priority
415 // ISRs not writable
416 case 0x100: case 0x110: case 0x120: case 0x130:
417 case 0x140: case 0x150: case 0x160: case 0x170:
418 // TMRs not writable
419 case 0x180: case 0x190: case 0x1a0: case 0x1b0:
420 case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
421 // IRRs not writable
422 case 0x200: case 0x210: case 0x220: case 0x230:
423 case 0x240: case 0x250: case 0x260: case 0x270:
424 // current count for timer
425 case 0x390:
426 // all read-only registers should fall into this line
427 BX_INFO(("warning: write to read-only APIC register 0x%x", apic_reg));
428 break;
429 default:
430 shadow_error_status |= APIC_ERR_ILLEGAL_ADDR;
431 // but for now I want to know about it in case I missed some.
432 BX_PANIC(("APIC register %x not implemented", apic_reg));
436 void bx_local_apic_c::send_ipi(void)
438 int dest = (icr_hi >> 24) & 0xff;
439 int dest_shorthand = (icr_lo >> 18) & 3;
440 int trig_mode = (icr_lo >> 15) & 1;
441 int level = (icr_lo >> 14) & 1;
442 int dest_mode = (icr_lo >> 11) & 1;
443 int delivery_mode = (icr_lo >> 8) & 7;
444 int vector = (icr_lo & 0xff);
445 int accepted = 0;
447 if(delivery_mode == APIC_DM_INIT)
449 if(level == 0 && trig_mode == 1) {
450 // special mode in local apic. See "INIT Level Deassert" in the
451 // Intel Soft. Devel. Guide Vol 3, page 7-34. This magic code
452 // causes all APICs(regardless of dest address) to set their
453 // arbitration ID to their APIC ID. Not supported by Pentium 4
454 // and Intel Xeon processors.
455 return; // we not model APIC bus arbitration ID anyway
459 switch(dest_shorthand) {
460 case 0: // no shorthand, use real destination value
461 accepted = apic_bus_deliver_interrupt(vector, dest, delivery_mode, dest_mode, level, trig_mode);
462 break;
463 case 1: // self
464 trigger_irq(vector, trig_mode);
465 accepted = 1;
466 break;
467 case 2: // all including self
468 accepted = apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, APIC_MAX_ID);
469 break;
470 case 3: // all but self
471 accepted = apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, get_id());
472 break;
473 default:
474 BX_PANIC(("Invalid desination shorthand %#x\n", dest_shorthand));
477 if(! accepted) {
478 BX_DEBUG(("An IPI wasn't accepted, raise APIC_ERR_TX_ACCEPT_ERR"));
479 shadow_error_status |= APIC_ERR_TX_ACCEPT_ERR;
483 void bx_local_apic_c::write_spurious_interrupt_register(Bit32u value)
485 BX_DEBUG(("write of %08x to spurious interrupt register", value));
487 #ifdef BX_IMPLEMENT_XAPIC
488 spurious_vector = value & 0xff;
489 #else
490 // bits 0-3 of the spurious vector hardwired to '1
491 spurious_vector = (value & 0xf0) | 0xf;
492 #endif
494 software_enabled = (value >> 8) & 1;
495 focus_disable = (value >> 9) & 1;
497 if(! software_enabled) {
498 for(unsigned i=0; i<APIC_LVT_ENTRIES; i++) {
499 lvt[i] |= 0x10000; // all LVT are masked
504 void bx_local_apic_c::receive_EOI(Bit32u value)
506 BX_DEBUG(("%s: Wrote 0x%x to EOI", cpu->name, value));
507 int vec = highest_priority_int(isr);
508 if(vec < 0) {
509 BX_DEBUG(("EOI written without any bit in ISR"));
510 } else {
511 if((Bit32u) vec != spurious_vector) {
512 BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu->name, vec));
513 isr[vec] = 0;
514 if(tmr[vec]) {
515 apic_bus_broadcast_eoi(vec);
516 tmr[vec] = 0;
518 service_local_apic();
522 if(bx_dbg.apic)
523 print_status();
526 void bx_local_apic_c::startup_msg(Bit32u vector)
528 if(cpu->debug_trap & BX_DEBUG_TRAP_SPECIAL) {
529 cpu->debug_trap &= ~BX_DEBUG_TRAP_SPECIAL;
530 cpu->gen_reg[BX_32BIT_REG_EIP].dword.erx = 0;
531 cpu->load_seg_reg(&cpu->sregs[BX_SEG_REG_CS], vector*0x100);
532 BX_INFO(("%s started up at %04X:%08X by APIC",
533 cpu->name, vector*0x100, cpu->get_eip()));
534 } else {
535 BX_INFO(("%s started up by APIC, but was not halted at the time", cpu->name));
539 // APIC read: 4 byte read from 16-byte aligned APIC address
540 void bx_local_apic_c::read_aligned(bx_phy_address addr, Bit32u *data)
542 BX_DEBUG(("%s: LAPIC read from address 0x" FMT_PHY_ADDRX, cpu->name, addr));
543 BX_ASSERT((addr & 0xf) == 0);
544 *data = 0; // default value for unimplemented registers
545 bx_phy_address apic_reg = addr & 0xff0;
546 switch(apic_reg) {
547 case 0x20: // local APIC id
548 *data = (id) << 24; break;
549 case 0x30: // local APIC version
550 *data = BX_LAPIC_VERSION_ID; break;
551 case 0x80: // task priority
552 *data = task_priority & 0xff; break;
553 case 0x90: // arbitration priority
554 *data = get_apr(); break;
555 case 0xa0: // processor priority
556 *data = get_ppr(); break;
557 case 0xb0: // EOI
559 * Read-modify-write operations should operate without generating
560 * exceptions, and are used by some operating systems to EOI.
561 * The results of reads should be ignored by the OS.
563 break;
564 case 0xd0: // logical destination
565 *data = (log_dest & APIC_ID_MASK) << 24; break;
566 case 0xe0: // destination format
567 *data = ((dest_format & 0xf) << 24) | 0x0fffffff; break;
568 case 0xf0: // spurious interrupt vector
570 Bit32u reg = spurious_vector;
571 if(software_enabled) reg |= 0x100;
572 if(focus_disable) reg |= 0x200;
573 *data = reg;
575 break;
576 case 0x100: case 0x110:
577 case 0x120: case 0x130:
578 case 0x140: case 0x150:
579 case 0x160: case 0x170:
581 unsigned index = (apic_reg - 0x100) << 1;
582 Bit32u value = 0, mask = 1;
583 for(int i=0;i<32;i++) {
584 if(isr[index+i]) value |= mask;
585 mask <<= 1;
587 *data = value;
589 break;
590 case 0x180: case 0x190:
591 case 0x1a0: case 0x1b0:
592 case 0x1c0: case 0x1d0:
593 case 0x1e0: case 0x1f0:
595 unsigned index = (apic_reg - 0x180) << 1;
596 Bit32u value = 0, mask = 1;
597 for(int i=0;i<32;i++) {
598 if(tmr[index+i]) value |= mask;
599 mask <<= 1;
601 *data = value;
603 break;
604 case 0x200: case 0x210:
605 case 0x220: case 0x230:
606 case 0x240: case 0x250:
607 case 0x260: case 0x270:
609 unsigned index = (apic_reg - 0x200) << 1;
610 Bit32u value = 0, mask = 1;
611 for(int i=0;i<32;i++) {
612 if(irr[index+i]) value |= mask;
613 mask <<= 1;
615 *data = value;
617 break;
618 case 0x280: // error status reg
619 *data = error_status; break;
620 case 0x300: // interrupt command reg 0-31
621 *data = icr_lo; break;
622 case 0x310: // interrupt command reg 31-63
623 *data = icr_hi; break;
624 case 0x320: // LVT Timer Reg
625 case 0x330: // LVT Thermal Monitor
626 case 0x340: // LVT Performance Counter
627 case 0x350: // LVT LINT0 Reg
628 case 0x360: // LVT Lint1 Reg
629 case 0x370: // LVT Error Reg
631 int index = (apic_reg - 0x320) >> 4;
632 *data = lvt[index];
633 break;
635 case 0x380: // initial count for timer
636 *data = timer_initial;
637 break;
638 case 0x390: // current count for timer
639 if(timer_active==0) {
640 *data = timer_current;
641 } else {
642 Bit64u delta64 = (bx_pc_system.time_ticks() - ticksInitial) / timer_divide_factor;
643 Bit32u delta32 = (Bit32u) delta64;
644 if(delta32 > timer_initial)
645 BX_PANIC(("APIC: R(curr timer count): delta < initial"));
646 timer_current = timer_initial - delta32;
647 *data = timer_current;
649 break;
650 case 0x3e0: // timer divide configuration
651 *data = timer_divconf;
652 break;
653 default:
654 BX_INFO(("APIC register %08x not implemented", apic_reg));
657 BX_DEBUG(("%s: read from APIC address 0x" FMT_PHY_ADDRX " = %08x", cpu->name, addr, *data));
660 int bx_local_apic_c::highest_priority_int(Bit8u *array)
662 for(int i=BX_LAPIC_LAST_VECTOR; i>=BX_LAPIC_FIRST_VECTOR; i--)
663 if(array[i]) return i;
665 return -1;
668 void bx_local_apic_c::service_local_apic(void)
670 if(bx_dbg.apic) {
671 BX_INFO(("service_local_apic()"));
672 print_status();
674 if(INTR) return; // INTR already up; do nothing
675 // find first interrupt in irr.
676 int first_irr = highest_priority_int(irr);
677 if (first_irr < 0) return; // no interrupts, leave INTR=0
678 int first_isr = highest_priority_int(isr);
679 if (first_isr >= 0 && first_irr <= first_isr) {
680 BX_DEBUG(("local apic(%s): not delivering int 0x%02x because int 0x%02x is in service", cpu->name, first_irr, first_isr));
681 return;
683 if(((Bit32u)(first_irr) & 0xf0) <= (task_priority & 0xf0)) {
684 BX_DEBUG(("local apic(%s): not delivering int 0x%02X because task_priority is 0x%02X", cpu->name, first_irr, task_priority));
685 return;
687 // interrupt has appeared in irr. Raise INTR. When the CPU
688 // acknowledges, we will run highest_priority_int again and
689 // return it.
690 BX_DEBUG(("service_local_apic(): setting INTR=1 for vector 0x%02x", first_irr));
691 INTR = 1;
692 cpu->async_event = 1;
695 bx_bool bx_local_apic_c::deliver(Bit8u vector, Bit8u delivery_mode, Bit8u trig_mode)
697 switch(delivery_mode) {
698 case APIC_DM_FIXED:
699 case APIC_DM_LOWPRI:
700 BX_DEBUG(("Deliver lowest priority of fixed interrupt vector %02x", vector));
701 trigger_irq(vector, trig_mode);
702 break;
703 case APIC_DM_SMI:
704 BX_INFO(("Deliver SMI"));
705 cpu->deliver_SMI();
706 return 1;
707 case APIC_DM_NMI:
708 BX_INFO(("Deliver NMI"));
709 cpu->deliver_NMI();
710 return 1;
711 case APIC_DM_INIT:
712 BX_INFO(("Deliver INIT IPI"));
713 cpu->deliver_INIT();
714 break;
715 case APIC_DM_SIPI:
716 BX_DEBUG(("Deliver Start Up IPI"));
717 startup_msg(vector);
718 break;
719 case APIC_DM_EXTINT:
720 BX_DEBUG(("Deliver EXTINT vector %02x", vector));
721 trigger_irq(vector, trig_mode, 1);
722 break;
723 default:
724 return 0;
727 return 1;
730 void bx_local_apic_c::trigger_irq(unsigned vector, unsigned trigger_mode, bx_bool bypass_irr_isr)
732 BX_DEBUG(("Local apic on %s: trigger interrupt vector=0x%x", cpu->name, vector));
734 if(vector > BX_LAPIC_LAST_VECTOR || vector < BX_LAPIC_FIRST_VECTOR) {
735 shadow_error_status |= APIC_ERR_RX_ILLEGAL_VEC;
736 BX_INFO(("bogus vector %#x, ignoring ...", vector));
737 return;
740 BX_DEBUG(("triggered vector %#02x", vector));
742 if(bypass_irr_isr) {
743 goto service_vector;
746 if(irr[vector] != 0) {
747 BX_DEBUG(("triggered vector %#02x not accepted", vector));
748 return;
751 service_vector:
752 irr[vector] = 1;
753 tmr[vector] = trigger_mode; // set for level triggered
754 service_local_apic();
757 void bx_local_apic_c::untrigger_irq(unsigned vector, unsigned trigger_mode)
759 BX_DEBUG(("Local apic on %s: untrigger interrupt vector=0x%x", cpu->name, vector));
760 // hardware says "no more". clear the bit. If the CPU hasn't yet
761 // acknowledged the interrupt, it will never be serviced.
762 BX_ASSERT(irr[vector] == 1);
763 irr[vector] = 0;
764 if(bx_dbg.apic) print_status();
767 Bit8u bx_local_apic_c::acknowledge_int(void)
769 // CPU calls this when it is ready to service one interrupt
770 if(!INTR)
771 BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu->name));
772 BX_ASSERT(INTR);
773 int vector = highest_priority_int(irr);
774 if (vector < 0) goto spurious;
775 if((vector & 0xf0) <= get_ppr()) goto spurious;
776 BX_ASSERT(irr[vector] == 1);
777 BX_DEBUG(("%s: acknowledge_int returning vector 0x%x", cpu->name, vector));
778 irr[vector] = 0;
779 isr[vector] = 1;
780 if(bx_dbg.apic) {
781 BX_INFO(("Status after setting isr:"));
782 print_status();
784 INTR = 0;
785 cpu->async_event = 1;
786 service_local_apic(); // will set INTR again if another is ready
787 return vector;
789 spurious:
790 INTR = 0;
791 cpu->async_event = 1;
792 return spurious_vector;
795 void bx_local_apic_c::print_status(void)
797 BX_INFO(("%s local apic: status is {:", cpu->name));
798 for(int vec=0; vec<BX_LAPIC_MAX_INTS; vec++) {
799 if(irr[vec] || isr[vec]) {
800 BX_INFO(("vec 0x%x: irr=%d, isr=%d", vec,(int)irr[vec],(int)isr[vec]));
803 BX_INFO(("}"));
806 bx_bool bx_local_apic_c::match_logical_addr(Bit8u address)
808 bx_bool match = 0;
810 if (dest_format == 0xf) {
811 // flat model
812 match = ((address & log_dest) != 0);
813 BX_DEBUG(("%s: comparing MDA %02x to my LDR %02x -> %s", cpu->name,
814 address, log_dest, match? "Match" : "Not a match"));
816 else if (dest_format == 0) {
817 // cluster model
818 if (address == 0xff) // broadcast all
819 return 1;
821 if ((unsigned)(address & 0xf0) == (unsigned)(log_dest & 0xf0))
822 match = ((address & log_dest & 0x0f) != 0);
824 else {
825 BX_PANIC(("bx_local_apic_c::match_logical_addr: unsupported dest format 0x%x", dest_format));
828 return match;
831 Bit8u bx_local_apic_c::get_ppr(void)
833 int ppr = highest_priority_int(isr);
835 if((ppr < 0) || ((task_priority & 0xF0) >= ((Bit32u) ppr & 0xF0)))
836 ppr = task_priority;
837 else
838 ppr &= 0xF0;
840 return ppr;
843 Bit8u bx_local_apic_c::get_tpr(void)
845 return task_priority;
848 void bx_local_apic_c::set_tpr(Bit8u priority)
850 if(priority < task_priority) {
851 task_priority = priority;
852 service_local_apic();
853 } else {
854 task_priority = priority;
858 Bit8u bx_local_apic_c::get_apr(void)
860 Bit32u tpr = (task_priority >> 4) & 0xf;
861 int first_isr = highest_priority_int(isr);
862 if (first_isr < 0) first_isr = 0;
863 int first_irr = highest_priority_int(irr);
864 if (first_irr < 0) first_irr = 0;
865 Bit32u isrv = (first_isr >> 4) & 0xf;
866 Bit32u irrv = (first_irr >> 4) & 0xf;
867 Bit8u apr;
869 if((tpr >= irrv) && (tpr > isrv)) {
870 apr = task_priority & 0xff;
872 else {
873 apr = ((tpr & isrv) > irrv) ?(tpr & isrv) : irrv;
874 apr <<= 4;
877 BX_DEBUG(("apr = %d\n", apr));
879 return(Bit8u) apr;
882 bx_bool bx_local_apic_c::is_focus(Bit8u vector)
884 if(focus_disable) return 0;
885 return(irr[vector] || isr[vector]) ? 1 : 0;
888 void bx_local_apic_c::periodic_smf(void *this_ptr)
890 bx_local_apic_c *class_ptr = (bx_local_apic_c *) this_ptr;
891 class_ptr->periodic();
894 void bx_local_apic_c::periodic(void)
896 if(!timer_active) {
897 BX_ERROR(("%s: bx_local_apic_c::periodic called, timer_active==0", cpu->name));
898 return;
901 // timer reached zero since the last call to periodic.
902 Bit32u timervec = lvt[APIC_LVT_TIMER];
903 if(timervec & 0x20000) {
904 // Periodic mode.
905 // If timer is not masked, trigger interrupt.
906 if((timervec & 0x10000)==0) {
907 trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
909 else {
910 BX_DEBUG(("%s: local apic timer LVT masked", cpu->name));
912 // Reload timer values.
913 timer_current = timer_initial;
914 ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
915 BX_DEBUG(("%s: local apic timer(periodic) triggered int, reset counter to 0x%08x", cpu->name, timer_current));
917 else {
918 // one-shot mode
919 timer_current = 0;
920 // If timer is not masked, trigger interrupt.
921 if((timervec & 0x10000)==0) {
922 trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
924 else {
925 BX_DEBUG(("%s: local apic timer LVT masked", cpu->name));
927 timer_active = 0;
928 BX_DEBUG(("%s: local apic timer(one-shot) triggered int", cpu->name));
929 bx_pc_system.deactivate_timer(timer_handle);
933 void bx_local_apic_c::set_divide_configuration(Bit32u value)
935 BX_ASSERT(value == (value & 0x0b));
936 // move bit 3 down to bit 0.
937 value = ((value & 8) >> 1) | (value & 3);
938 BX_ASSERT(value >= 0 && value <= 7);
939 timer_divide_factor = (value==7)? 1 : (2 << value);
940 BX_INFO(("%s: set timer divide factor to %d", cpu->name, timer_divide_factor));
943 void bx_local_apic_c::set_initial_timer_count(Bit32u value)
945 // If active before, deactive the current timer before changing it.
946 if(timer_active) {
947 bx_pc_system.deactivate_timer(timer_handle);
948 timer_active = 0;
951 timer_initial = value;
952 timer_current = 0;
954 if(timer_initial != 0) // terminate the counting if timer_initial = 0
956 // This should trigger the counter to start. If already started,
957 // restart from the new start value.
958 BX_DEBUG(("APIC: Initial Timer Count Register = %u", value));
959 timer_current = timer_initial;
960 timer_active = 1;
961 Bit32u timervec = lvt[APIC_LVT_TIMER];
962 bx_bool continuous = (timervec & 0x20000) > 0;
963 ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
964 bx_pc_system.activate_timer_ticks(timer_handle,
965 Bit64u(timer_initial) * Bit64u(timer_divide_factor), continuous);
969 void bx_local_apic_c::register_state(bx_param_c *parent)
971 unsigned i;
972 char name[6];
974 bx_list_c *lapic = new bx_list_c(parent, "local_apic", 25);
976 BXRS_HEX_PARAM_SIMPLE(lapic, base_addr);
977 BXRS_HEX_PARAM_SIMPLE(lapic, id);
978 BXRS_HEX_PARAM_SIMPLE(lapic, spurious_vector);
979 BXRS_PARAM_BOOL(lapic, software_enabled, software_enabled);
980 BXRS_PARAM_BOOL(lapic, focus_disable, focus_disable);
981 BXRS_HEX_PARAM_SIMPLE(lapic, task_priority);
982 BXRS_HEX_PARAM_SIMPLE(lapic, spurious_vector);
983 BXRS_HEX_PARAM_SIMPLE(lapic, log_dest);
984 BXRS_HEX_PARAM_SIMPLE(lapic, dest_format);
986 bx_list_c *ISR = new bx_list_c(lapic, "isr", BX_LAPIC_MAX_INTS);
987 bx_list_c *TMR = new bx_list_c(lapic, "tmr", BX_LAPIC_MAX_INTS);
988 bx_list_c *IRR = new bx_list_c(lapic, "irr", BX_LAPIC_MAX_INTS);
989 for (i=0; i<BX_LAPIC_MAX_INTS; i++) {
990 sprintf(name, "0x%02x", i);
991 new bx_shadow_num_c(ISR, name, &isr[i]);
992 new bx_shadow_num_c(TMR, name, &tmr[i]);
993 new bx_shadow_num_c(IRR, name, &irr[i]);
996 BXRS_HEX_PARAM_SIMPLE(lapic, error_status);
997 BXRS_HEX_PARAM_SIMPLE(lapic, shadow_error_status);
998 BXRS_HEX_PARAM_SIMPLE(lapic, icr_hi);
999 BXRS_HEX_PARAM_SIMPLE(lapic, icr_lo);
1001 bx_list_c *LVT = new bx_list_c(lapic, "lvt", APIC_LVT_ENTRIES);
1002 for (i=0; i<APIC_LVT_ENTRIES; i++) {
1003 sprintf(name, "%d", i);
1004 new bx_shadow_num_c(LVT, name, &lvt[i], BASE_HEX);
1007 BXRS_HEX_PARAM_SIMPLE(lapic, timer_initial);
1008 BXRS_HEX_PARAM_SIMPLE(lapic, timer_current);
1009 BXRS_HEX_PARAM_SIMPLE(lapic, timer_divconf);
1010 BXRS_DEC_PARAM_SIMPLE(lapic, timer_divide_factor);
1011 BXRS_PARAM_BOOL(lapic, timer_active, timer_active);
1012 BXRS_HEX_PARAM_SIMPLE(lapic, ticksInitial);
1013 BXRS_PARAM_BOOL(lapic, INTR, INTR);
1016 #endif /* if BX_SUPPORT_APIC */