- tftp_send_optack() was not 64-bit clean (patch from SF bug #1787500)
[bochs-mirror.git] / cpu / apic.cc
blob056e17d929501febe2b66d7c2d2210f567a0fd3a
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: apic.cc,v 1.96 2007/04/04 16:55:50 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2001 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #define NEED_CPU_REG_SHORTCUTS 1
29 #include "bochs.h"
30 #include "cpu.h"
31 #include "iodev/iodev.h"
33 #if BX_SUPPORT_APIC
35 #define LOG_THIS this->
37 #define APIC_BROADCAST_PHYSICAL_DESTINATION_MODE (APIC_MAX_ID)
39 #define BX_LAPIC_FIRST_VECTOR 0x10
40 #define BX_LAPIC_LAST_VECTOR 0xfe
42 ///////////// APIC BUS /////////////
44 int apic_bus_deliver_interrupt(Bit8u vector, Bit8u dest, Bit8u delivery_mode, Bit8u dest_mode, bx_bool level, bx_bool trig_mode)
46 if(delivery_mode == APIC_DM_LOWPRI)
48 if(dest_mode == 0) {
49 // I/O subsytem initiated interrupt with lowest priority delivery
50 // mode is not supported in physical destination mode
51 // BX_ERROR(("Ignoring lowest priority interrupt in physical dest mode !"));
52 return 0;
54 else {
55 return apic_bus_deliver_lowest_priority(vector, dest, trig_mode, 0);
59 // determine destination local apics and deliver
60 if(dest_mode == 0) {
61 if(dest == APIC_BROADCAST_PHYSICAL_DESTINATION_MODE)
63 return apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, APIC_MAX_ID);
65 else {
66 // the destination is single agent
67 for (unsigned i=0;i<BX_NUM_LOCAL_APICS;i++)
69 if(BX_CPU_APIC(i)->get_id() == dest) {
70 BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
71 return 1;
75 return 0;
78 else {
79 // logical destination mode
80 if(dest == 0) return 0;
82 bx_bool interrupt_delivered = 0;
84 for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
85 if(BX_CPU_APIC(i)->match_logical_addr(dest)) {
86 BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
87 interrupt_delivered = 1;
91 return interrupt_delivered;
95 int apic_bus_deliver_lowest_priority(Bit8u vector, Bit8u dest, bx_bool trig_mode, bx_bool broadcast)
97 int i;
99 #ifndef BX_IMPLEMENT_XAPIC
100 // search for if focus processor exists
101 for (i=0; i<BX_NUM_LOCAL_APICS; i++) {
102 if(BX_CPU_APIC(i)->is_focus(vector)) {
103 BX_CPU_APIC(i)->deliver(vector, APIC_DM_LOWPRI, trig_mode);
104 return 1;
107 #endif
109 // focus processor not found, looking for lowest priority agent
110 int lowest_priority_agent = -1, lowest_priority = 0x100;
112 for (i=0; i<BX_NUM_LOCAL_APICS; i++) {
113 if(broadcast || BX_CPU_APIC(i)->match_logical_addr(dest)) {
114 #ifndef BX_IMPLEMENT_XAPIC
115 int priority = BX_CPU_APIC(i)->get_apr();
116 #else
117 int priority = BX_CPU_APIC(i)->get_tpr();
118 #endif
119 if(priority < lowest_priority) {
120 lowest_priority = priority;
121 lowest_priority_agent = i;
126 if(lowest_priority_agent >= 0)
128 BX_CPU_APIC(lowest_priority_agent)->deliver(vector, APIC_DM_LOWPRI, trig_mode);
129 return 1;
132 return 0;
135 int apic_bus_broadcast_interrupt(Bit8u vector, Bit8u delivery_mode, bx_bool trig_mode, int exclude_cpu)
137 if(delivery_mode == APIC_DM_LOWPRI)
139 return apic_bus_deliver_lowest_priority(vector, 0 /* doesn't matter */, trig_mode, 1);
142 // deliver to all bus agents except 'exclude_cpu'
143 for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
144 if(i == exclude_cpu) continue;
145 BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
148 return 1;
151 static void apic_bus_broadcast_eoi(Bit8u vector)
153 bx_devices.ioapic->receive_eoi(vector);
156 ////////////////////////////////////
158 bx_generic_apic_c::bx_generic_apic_c(bx_phy_address base)
160 put("APIC?");
161 settype(APICLOG);
162 id = APIC_UNKNOWN_ID;
163 set_base(base);
166 void bx_generic_apic_c::set_base(bx_phy_address newbase)
168 newbase &= (~0xfff);
169 base_addr = newbase;
170 if (id != APIC_UNKNOWN_ID)
171 BX_INFO(("relocate APIC id=%d to 0x%08x", id, newbase));
174 void bx_generic_apic_c::set_id(Bit8u newid)
176 BX_INFO(("set APIC ID to %d", newid));
177 id = newid;
180 bx_bool bx_generic_apic_c::is_selected(bx_phy_address addr, unsigned len)
182 if((addr & ~0xfff) == get_base()) {
183 if((addr & 0xf) != 0)
184 BX_INFO(("warning: misaligned APIC access. addr=0x%08x, len=%d", addr, len));
185 return 1;
187 return 0;
190 void bx_generic_apic_c::read(bx_phy_address addr, void *data, unsigned len)
192 if((addr & ~0x3) != ((addr+len-1) & ~0x3)) {
193 BX_PANIC(("APIC read at address 0x%08x spans 32-bit boundary !", addr));
194 return;
196 Bit32u value;
197 read_aligned(addr & ~0x3, &value);
198 if(len == 4) { // must be 32-bit aligned
199 *((Bit32u *)data) = value;
200 return;
202 // handle partial read, independent of endian-ness
203 value >>= (addr&3)*8;
204 if (len == 1)
205 *((Bit8u *) data) = value & 0xff;
206 else if (len == 2)
207 *((Bit16u *)data) = value & 0xffff;
208 else
209 BX_PANIC(("Unsupported APIC read at address 0x%08x, len=%d", addr, len));
212 void bx_generic_apic_c::write(bx_phy_address addr, void *data, unsigned len)
214 if((addr & ~0x3) != ((addr+len-1) & ~0x3)) {
215 BX_PANIC(("APIC write at address 0x%08x spans 32-bit boundary !", addr));
216 return;
218 bx_phy_address addr_aligned = addr & ~0x3;
219 if(len == 4) { // must be 32-bit aligned
220 write_aligned(addr_aligned, (Bit32u*) data);
221 return;
223 // partial write to the apic register, need to update some bytes
224 // and do not touch the others, i.e. to do RMW operation
225 Bit32u value;
226 read_aligned(addr_aligned, &value); // apic read has no side effects
227 // handle partial write, independent of endian-ness
228 unsigned shift = (addr&3)*8;
229 if (len == 1) {
230 value &= ~(0xff << shift);
231 value |= (*((Bit8u *) data) << shift);
233 else if (len == 2) {
234 value &= ~(0xffff << shift);
235 value |= (*((Bit16u *)data) << shift);
237 else {
238 BX_PANIC(("Unsupported APIC write at address 0x%08x, len=%d", addr, len));
240 write_aligned(addr_aligned, &value);
243 bx_local_apic_c::bx_local_apic_c(BX_CPU_C *mycpu)
244 : bx_generic_apic_c(BX_LAPIC_BASE_ADDR), cpu(mycpu), cpu_id(cpu->which_cpu())
246 // KPL: Register a non-active timer for use when the timer is started.
247 timer_handle = bx_pc_system.register_timer_ticks(this,
248 BX_CPU(0)->local_apic.periodic_smf, 0, 0, 0, "lapic");
249 timer_active = 0;
251 reset();
253 INTR = 0;
256 void bx_local_apic_c::reset()
258 /* same as INIT but also sets arbitration ID and APIC ID */
259 init();
262 void bx_local_apic_c::init()
264 int i;
266 bx_generic_apic_c::init();
268 BX_INFO(("local apic in %s initializing",
269 (cpu && cpu->name) ? cpu->name : "?"));
271 // default address for a local APIC, can be moved
272 base_addr = BX_LAPIC_BASE_ADDR;
273 error_status = shadow_error_status = 0;
274 log_dest = 0;
275 dest_format = 0xf;
276 icr_hi = 0;
277 icr_lo = 0;
278 log_dest = 0;
279 task_priority = 0;
281 for(i=0; i<BX_LAPIC_MAX_INTS; i++) {
282 irr[i] = isr[i] = tmr[i] = 0;
285 timer_divconf = 0;
286 timer_divide_factor = 1;
287 timer_initial = 0;
288 timer_current = 0;
290 if(timer_active) {
291 bx_pc_system.deactivate_timer(timer_handle);
292 timer_active = 0;
295 for(i=0; i<APIC_LVT_ENTRIES; i++) {
296 lvt[i] = 0x10000; // all LVT are masked
299 spurious_vector = 0xff; // software disabled(bit 8)
300 software_enabled = 0;
301 focus_disable = 0;
304 void bx_local_apic_c::set_id(Bit8u newid)
306 bx_generic_apic_c::set_id(newid);
307 sprintf(cpu->name, "CPU apicid=%02x",(Bit32u)id);
308 if(id < APIC_MAX_ID) {
309 char buffer[16];
310 sprintf(buffer, "APIC%x", id);
311 put(buffer);
312 settype(CPU0LOG + id);
313 sprintf(buffer, "CPU%x", id);
314 cpu->put(buffer);
315 } else {
316 BX_INFO(("naming convention for apics requires id=0-%d only", APIC_MAX_ID));
318 if(BX_CPU_LEVEL<2)
319 BX_INFO(("8086"));
320 else
321 BX_INFO(("80%d86", BX_CPU_LEVEL));
324 // APIC write: 4 byte write to 16-byte aligned APIC address
325 void bx_local_apic_c::write_aligned(bx_phy_address addr, Bit32u *data)
327 BX_DEBUG(("%s: LAPIC write 0x%08x to address %08x", cpu->name, *data, addr));
328 BX_ASSERT((addr & 0xf) == 0);
329 addr &= 0xff0;
330 Bit32u value = *data;
331 switch(addr) {
332 case 0x20: // local APIC id
333 id = (value>>24) & APIC_ID_MASK;
334 break;
335 case 0x80: // task priority
336 set_tpr(value & 0xff);
337 break;
338 case 0xb0: // EOI
339 receive_EOI(value);
340 break;
341 case 0xd0: // logical destination
342 log_dest = (value >> 24) & APIC_ID_MASK;
343 BX_DEBUG(("set logical destination to %02x", log_dest));
344 break;
345 case 0xe0: // destination format
346 dest_format = (value >> 28) & 0xf;
347 BX_DEBUG(("set destination format to %02x", dest_format));
348 break;
349 case 0xf0: // spurious interrupt vector
350 write_spurious_interrupt_register(value);
351 break;
352 case 0x280: // error status reg
353 // Here's what the IA-devguide-3 says on p.7-45:
354 // The ESR is a read/write register and is reset after being written to
355 // by the processor. A write to the ESR must be done just prior to
356 // reading the ESR to allow the register to be updated.
357 error_status = shadow_error_status;
358 shadow_error_status = 0;
359 break;
360 case 0x300: // interrupt command reg 0-31
361 icr_lo = value & ~(1<<12); // force delivery status bit = 0(idle)
362 send_ipi();
363 break;
364 case 0x310: // interrupt command reg 31-63
365 icr_hi = value & 0xff000000;
366 break;
367 case 0x320: // LVT Timer Reg
368 lvt[APIC_LVT_TIMER] = value & 0x300ff;
369 if(! software_enabled) lvt[APIC_LVT_TIMER] |= 0x10000;
370 break;
371 case 0x330: // LVT Thermal Monitor
372 lvt[APIC_LVT_THERMAL] = value & 0x107ff;
373 if(! software_enabled) lvt[APIC_LVT_THERMAL] |= 0x10000;
374 break;
375 case 0x340: // LVT Performance Counter
376 lvt[APIC_LVT_PERFORM] = value & 0x107ff;
377 if(! software_enabled) lvt[APIC_LVT_PERFORM] |= 0x10000;
378 break;
379 case 0x350: // LVT LINT0 Reg
380 lvt[APIC_LVT_LINT0] = value & 0x1a7ff;
381 if(! software_enabled) lvt[APIC_LVT_LINT0] |= 0x10000;
382 break;
383 case 0x360: // LVT Lint1 Reg
384 lvt[APIC_LVT_LINT1] = value & 0x1a7ff;
385 if(! software_enabled) lvt[APIC_LVT_LINT1] |= 0x10000;
386 break;
387 case 0x370: // LVT Error Reg
388 lvt[APIC_LVT_ERROR] = value & 0x100ff;
389 if(! software_enabled) lvt[APIC_LVT_ERROR] |= 0x10000;
390 break;
391 case 0x380: // initial count for timer
392 set_initial_timer_count(value);
393 break;
394 case 0x3e0: // timer divide configuration
395 // only bits 3, 1, and 0 are writable
396 timer_divconf = value & 0xb;
397 set_divide_configuration(timer_divconf);
398 break;
399 /* all read-only registers go here */
400 case 0x30: // local APIC version
401 case 0x90: // arbitration priority
402 case 0xa0: // processor priority
403 // ISRs not writable
404 case 0x100: case 0x110: case 0x120: case 0x130:
405 case 0x140: case 0x150: case 0x160: case 0x170:
406 // TMRs not writable
407 case 0x180: case 0x190: case 0x1a0: case 0x1b0:
408 case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
409 // IRRs not writable
410 case 0x200: case 0x210: case 0x220: case 0x230:
411 case 0x240: case 0x250: case 0x260: case 0x270:
412 // current count for timer
413 case 0x390:
414 // all read-only registers should fall into this line
415 BX_INFO(("warning: write to read-only APIC register 0x%02x", addr));
416 break;
417 default:
418 shadow_error_status |= APIC_ERR_ILLEGAL_ADDR;
419 // but for now I want to know about it in case I missed some.
420 BX_PANIC(("APIC register %08x not implemented", addr));
424 void bx_local_apic_c::send_ipi(void)
426 int dest = (icr_hi >> 24) & 0xff;
427 int dest_shorthand = (icr_lo >> 18) & 3;
428 int trig_mode = (icr_lo >> 15) & 1;
429 int level = (icr_lo >> 14) & 1;
430 int dest_mode = (icr_lo >> 11) & 1;
431 int delivery_mode = (icr_lo >> 8) & 7;
432 int vector = (icr_lo & 0xff);
433 int accepted = 0;
435 if(delivery_mode == APIC_DM_INIT)
437 if(level == 0 && trig_mode == 1) {
438 // special mode in local apic. See "INIT Level Deassert" in the
439 // Intel Soft. Devel. Guide Vol 3, page 7-34. This magic code
440 // causes all APICs(regardless of dest address) to set their
441 // arbitration ID to their APIC ID. Not supported by Pentium 4
442 // and Intel Xeon processors.
443 return; // we not model APIC bus arbitration ID anyway
447 switch(dest_shorthand) {
448 case 0: // no shorthand, use real destination value
449 accepted = apic_bus_deliver_interrupt(vector, dest, delivery_mode, dest_mode, level, trig_mode);
450 break;
451 case 1: // self
452 trigger_irq(vector, trig_mode);
453 accepted = 1;
454 break;
455 case 2: // all including self
456 accepted = apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, APIC_MAX_ID);
457 break;
458 case 3: // all but self
459 accepted = apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, get_id());
460 break;
461 default:
462 BX_PANIC(("Invalid desination shorthand %#x\n", dest_shorthand));
465 if(! accepted) {
466 BX_DEBUG(("An IPI wasn't accepted, raise APIC_ERR_TX_ACCEPT_ERR"));
467 shadow_error_status |= APIC_ERR_TX_ACCEPT_ERR;
471 void bx_local_apic_c::write_spurious_interrupt_register(Bit32u value)
473 BX_DEBUG(("write %08x to spurious interrupt register", value));
475 #ifdef BX_IMPLEMENT_XAPIC
476 spurious_vector = value & 0xff;
477 #else
478 // bits 0-3 of the spurious vector hardwired to '1
479 spurious_vector = (value & 0xf0) | 0xf;
480 #endif
482 software_enabled = (value >> 8) & 1;
483 focus_disable = (value >> 9) & 1;
485 if(! software_enabled) {
486 for(unsigned i=0; i<APIC_LVT_ENTRIES; i++) {
487 lvt[i] |= 0x10000; // all LVT are masked
492 void bx_local_apic_c::receive_EOI(Bit32u value)
494 BX_DEBUG(("%s: Wrote 0x%x to EOI", cpu->name, value));
495 int vec = highest_priority_int(isr);
496 if(vec < 0) {
497 BX_DEBUG(("EOI written without any bit in ISR"));
498 } else {
499 if((Bit32u) vec != spurious_vector) {
500 BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu->name, vec));
501 isr[vec] = 0;
502 if(tmr[vec]) {
503 apic_bus_broadcast_eoi(vec);
504 tmr[vec] = 0;
506 service_local_apic();
510 if(bx_dbg.apic)
511 print_status();
514 void bx_local_apic_c::startup_msg(Bit32u vector)
516 if(cpu->debug_trap & BX_DEBUG_TRAP_HALT_STATE) {
517 cpu->debug_trap &= ~BX_DEBUG_TRAP_HALT_STATE;
518 cpu->dword.eip = 0;
519 cpu->load_seg_reg(&cpu->sregs[BX_SEG_REG_CS], vector*0x100);
520 BX_INFO(("%s started up at %04X:%08X by APIC", cpu->name, vector*0x100, cpu->dword.eip));
521 } else {
522 BX_INFO(("%s started up by APIC, but was not halted at the time", cpu->name));
526 // APIC read: 4 byte read from 16-byte aligned APIC address
527 void bx_local_apic_c::read_aligned(bx_phy_address addr, Bit32u *data)
529 BX_DEBUG(("%s: LAPIC read from address %08x", cpu->name, addr));
530 BX_ASSERT((addr & 0xf) == 0);
531 *data = 0; // default value for unimplemented registers
532 bx_phy_address addr2 = addr & 0xff0;
533 switch(addr2) {
534 case 0x20: // local APIC id
535 *data = (id) << 24; break;
536 case 0x30: // local APIC version
537 *data = BX_LAPIC_VERSION_ID; break;
538 case 0x80: // task priority
539 *data = task_priority & 0xff; break;
540 case 0x90: // arbitration priority
541 *data = get_apr(); break;
542 case 0xa0: // processor priority
543 *data = get_ppr(); break;
544 case 0xb0: // EOI
546 * Read-modify-write operations should operate without generating
547 * exceptions, and are used by some operating systems to EOI.
548 * The results of reads should be ignored by the OS.
550 break;
551 case 0xd0: // logical destination
552 *data = (log_dest & APIC_ID_MASK) << 24; break;
553 case 0xe0: // destination format
554 *data = ((dest_format & 0xf) << 24) | 0x0fffffff; break;
555 case 0xf0: // spurious interrupt vector
557 Bit32u reg = spurious_vector;
558 if(software_enabled) reg |= 0x100;
559 if(focus_disable) reg |= 0x200;
560 *data = reg;
562 break;
563 case 0x100: case 0x110:
564 case 0x120: case 0x130:
565 case 0x140: case 0x150:
566 case 0x160: case 0x170:
568 unsigned index = (addr2 - 0x100) << 1;
569 Bit32u value = 0, mask = 1;
570 for(int i=0;i<32;i++) {
571 if(isr[index+i]) value |= mask;
572 mask <<= 1;
574 *data = value;
576 break;
577 case 0x180: case 0x190:
578 case 0x1a0: case 0x1b0:
579 case 0x1c0: case 0x1d0:
580 case 0x1e0: case 0x1f0:
582 unsigned index = (addr2 - 0x180) << 1;
583 Bit32u value = 0, mask = 1;
584 for(int i=0;i<32;i++) {
585 if(tmr[index+i]) value |= mask;
586 mask <<= 1;
588 *data = value;
590 break;
591 case 0x200: case 0x210:
592 case 0x220: case 0x230:
593 case 0x240: case 0x250:
594 case 0x260: case 0x270:
596 unsigned index = (addr2 - 0x200) << 1;
597 Bit32u value = 0, mask = 1;
598 for(int i=0;i<32;i++) {
599 if(irr[index+i]) value |= mask;
600 mask <<= 1;
602 *data = value;
604 break;
605 case 0x280: // error status reg
606 *data = error_status; break;
607 case 0x300: // interrupt command reg 0-31
608 *data = icr_lo; break;
609 case 0x310: // interrupt command reg 31-63
610 *data = icr_hi; break;
611 case 0x320: // LVT Timer Reg
612 case 0x330: // LVT Thermal Monitor
613 case 0x340: // LVT Performance Counter
614 case 0x350: // LVT LINT0 Reg
615 case 0x360: // LVT Lint1 Reg
616 case 0x370: // LVT Error Reg
618 int index = (addr2 - 0x320) >> 4;
619 *data = lvt[index];
620 break;
622 case 0x380: // initial count for timer
623 *data = timer_initial;
624 break;
625 case 0x390: // current count for timer
626 if(timer_active==0) {
627 *data = timer_current;
628 } else {
629 Bit64u delta64;
630 Bit32u delta32;
631 delta64 = (bx_pc_system.time_ticks() - ticksInitial) / timer_divide_factor;
632 delta32 = (Bit32u) delta64;
633 if(delta32 > timer_initial)
634 BX_PANIC(("APIC: R(curr timer count): delta < initial"));
635 timer_current = timer_initial - delta32;
636 *data = timer_current;
638 break;
639 case 0x3e0: // timer divide configuration
640 *data = timer_divconf;
641 break;
642 default:
643 BX_INFO(("APIC register %08x not implemented", addr));
646 BX_DEBUG(("%s: read from APIC address %08x = %08x", cpu->name, addr, *data));
649 int bx_local_apic_c::highest_priority_int(Bit8u *array)
651 for(int i=BX_LAPIC_LAST_VECTOR; i>=BX_LAPIC_FIRST_VECTOR; i--)
652 if(array[i]) return i;
654 return -1;
657 void bx_local_apic_c::service_local_apic(void)
659 if(bx_dbg.apic) {
660 BX_INFO(("service_local_apic()"));
661 print_status();
663 if(INTR) return; // INTR already up; do nothing
664 // find first interrupt in irr.
665 int first_irr = highest_priority_int(irr);
666 if (first_irr < 0) return; // no interrupts, leave INTR=0
667 int first_isr = highest_priority_int(isr);
668 if (first_isr >= 0 && first_irr <= first_isr) {
669 BX_DEBUG(("local apic(%s): not delivering int 0x%02x because int 0x%02x is in service", cpu->name, first_irr, first_isr));
670 return;
672 if(((Bit32u)(first_irr) & 0xf0) <= (task_priority & 0xf0)) {
673 BX_DEBUG(("local apic(%s): not delivering int 0x%02X because task_priority is 0x%02X", cpu->name, first_irr, task_priority));
674 return;
676 // interrupt has appeared in irr. Raise INTR. When the CPU
677 // acknowledges, we will run highest_priority_int again and
678 // return it.
679 BX_DEBUG(("service_local_apic(): setting INTR=1 for vector 0x%02x", first_irr));
680 INTR = 1;
681 cpu->async_event = 1;
684 bx_bool bx_local_apic_c::deliver(Bit8u vector, Bit8u delivery_mode, Bit8u trig_mode)
686 switch(delivery_mode) {
687 case APIC_DM_FIXED:
688 case APIC_DM_LOWPRI:
689 BX_DEBUG(("Deliver lowest priority of fixed interrupt vector %02x", vector));
690 trigger_irq(vector, trig_mode);
691 break;
692 case APIC_DM_SMI:
693 BX_PANIC(("Delivery of SMI still not implemented !"));
694 cpu->deliver_SMI();
695 return 1;
696 case APIC_DM_NMI:
697 BX_PANIC(("Delivery of NMI still not implemented !"));
698 cpu->deliver_NMI();
699 return 1;
700 case APIC_DM_INIT:
701 BX_DEBUG(("Deliver INIT IPI"));
702 init();
703 break;
704 case APIC_DM_SIPI:
705 BX_DEBUG(("Deliver Start Up IPI"));
706 startup_msg(vector);
707 break;
708 case APIC_DM_EXTINT:
709 BX_DEBUG(("Deliver EXTINT vector %02x", vector));
710 trigger_irq(vector, trig_mode, 1);
711 break;
712 default:
713 return 0;
716 return 1;
719 void bx_local_apic_c::trigger_irq(unsigned vector, unsigned trigger_mode, bx_bool bypass_irr_isr)
721 BX_DEBUG(("Local apic on %s: trigger interrupt vector=0x%x", cpu->name, vector));
723 if(vector > BX_LAPIC_LAST_VECTOR || vector < BX_LAPIC_FIRST_VECTOR) {
724 shadow_error_status |= APIC_ERR_RX_ILLEGAL_VEC;
725 BX_INFO(("bogus vector %#x, ignoring ...", vector));
726 return;
729 BX_DEBUG(("triggered vector %#02x", vector));
731 if(bypass_irr_isr) {
732 goto service_vector;
735 if(irr[vector] != 0) {
736 BX_DEBUG(("triggered vector %#02x not accepted", vector));
737 return;
740 service_vector:
741 irr[vector] = 1;
742 tmr[vector] = trigger_mode; // set for level triggered
743 service_local_apic();
746 void bx_local_apic_c::untrigger_irq(unsigned vector, unsigned trigger_mode)
748 BX_DEBUG(("Local apic on %s: untrigger interrupt vector=0x%x", cpu->name, vector));
749 // hardware says "no more". clear the bit. If the CPU hasn't yet
750 // acknowledged the interrupt, it will never be serviced.
751 BX_ASSERT(irr[vector] == 1);
752 irr[vector] = 0;
753 if(bx_dbg.apic) print_status();
756 Bit8u bx_local_apic_c::acknowledge_int(void)
758 // CPU calls this when it is ready to service one interrupt
759 if(!INTR)
760 BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu->name));
761 BX_ASSERT(INTR);
762 int vector = highest_priority_int(irr);
763 if (vector < 0) goto spurious;
764 if((vector & 0xf0) <= get_ppr()) goto spurious;
765 BX_ASSERT(irr[vector] == 1);
766 BX_DEBUG(("%s: acknowledge_int returning vector 0x%x", cpu->name, vector));
767 irr[vector] = 0;
768 isr[vector] = 1;
769 if(bx_dbg.apic) {
770 BX_INFO(("Status after setting isr:"));
771 print_status();
773 INTR = 0;
774 cpu->async_event = 1;
775 service_local_apic(); // will set INTR again if another is ready
776 return vector;
778 spurious:
779 INTR = 0;
780 cpu->async_event = 1;
781 return spurious_vector;
784 void bx_local_apic_c::print_status(void)
786 BX_INFO(("%s local apic: status is {:", cpu->name));
787 for(int vec=0; vec<BX_LAPIC_MAX_INTS; vec++) {
788 if(irr[vec] || isr[vec]) {
789 BX_INFO(("vec 0x%x: irr=%d, isr=%d", vec,(int)irr[vec],(int)isr[vec]));
792 BX_INFO(("}"));
795 bx_bool bx_local_apic_c::match_logical_addr(Bit8u address)
797 if(dest_format != 0xf) {
798 BX_PANIC(("bx_local_apic_c::match_logical_addr: cluster model addressing not implemented"));
800 bx_bool match = ((address & log_dest) != 0);
801 BX_DEBUG(("%s: comparing MDA %02x to my LDR %02x -> %s", cpu->name,
802 address, log_dest, match? "Match" : "Not a match"));
803 return match;
806 Bit8u bx_local_apic_c::get_ppr(void)
808 int ppr = highest_priority_int(isr);
810 if((ppr < 0) || ((task_priority & 0xF0) >= ((Bit32u) ppr & 0xF0)))
811 ppr = task_priority;
812 else
813 ppr &= 0xF0;
815 return ppr;
818 Bit8u bx_local_apic_c::get_tpr(void)
820 return task_priority;
823 void bx_local_apic_c::set_tpr(Bit8u priority)
825 if(priority < task_priority) {
826 task_priority = priority;
827 service_local_apic();
828 } else {
829 task_priority = priority;
833 Bit8u bx_local_apic_c::get_apr(void)
835 Bit32u tpr = (task_priority >> 4) & 0xf;
836 Bit32u isrv = (highest_priority_int(isr) >> 4) & 0xf;
837 Bit32u irrv = (highest_priority_int(irr) >> 4) & 0xf;
838 Bit8u apr;
840 if(isrv < 0) isrv = 0;
841 if(irrv < 0) irrv = 0;
843 if((tpr >= irrv) && (tpr > isrv)) {
844 apr = task_priority & 0xff;
846 else {
847 apr = ((tpr & isrv) > irrv) ?(tpr & isrv) : irrv;
848 apr <<= 4;
851 BX_DEBUG(("apr = %d\n", apr));
853 return(Bit8u) apr;
856 bx_bool bx_local_apic_c::is_focus(Bit8u vector)
858 if(focus_disable) return 0;
859 return(irr[vector] || isr[vector]) ? 1 : 0;
862 void bx_local_apic_c::periodic_smf(void *this_ptr)
864 bx_local_apic_c *class_ptr = (bx_local_apic_c *) this_ptr;
865 class_ptr->periodic();
868 void bx_local_apic_c::periodic(void)
870 if(!timer_active) {
871 BX_ERROR(("%s: bx_local_apic_c::periodic called, timer_active==0", cpu->name));
872 return;
875 // timer reached zero since the last call to periodic.
876 Bit32u timervec = lvt[APIC_LVT_TIMER];
877 if(timervec & 0x20000) {
878 // Periodic mode.
879 // If timer is not masked, trigger interrupt.
880 if((timervec & 0x10000)==0) {
881 trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
883 else {
884 BX_DEBUG(("%s: local apic timer LVT masked", cpu->name));
886 // Reload timer values.
887 timer_current = timer_initial;
888 ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
889 BX_DEBUG(("%s: local apic timer(periodic) triggered int, reset counter to 0x%08x", cpu->name, timer_current));
891 else {
892 // one-shot mode
893 timer_current = 0;
894 // If timer is not masked, trigger interrupt.
895 if((timervec & 0x10000)==0) {
896 trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
898 else {
899 BX_DEBUG(("%s: local apic timer LVT masked", cpu->name));
901 timer_active = 0;
902 BX_DEBUG(("%s: local apic timer(one-shot) triggered int", cpu->name));
903 bx_pc_system.deactivate_timer(timer_handle);
907 void bx_local_apic_c::set_divide_configuration(Bit32u value)
909 BX_ASSERT(value == (value & 0x0b));
910 // move bit 3 down to bit 0.
911 value = ((value & 8) >> 1) | (value & 3);
912 BX_ASSERT(value >= 0 && value <= 7);
913 timer_divide_factor = (value==7)? 1 : (2 << value);
914 BX_INFO(("%s: set timer divide factor to %d", cpu->name, timer_divide_factor));
917 void bx_local_apic_c::set_initial_timer_count(Bit32u value)
919 // If active before, deactive the current timer before changing it.
920 if(timer_active) {
921 bx_pc_system.deactivate_timer(timer_handle);
922 timer_active = 0;
925 timer_initial = value;
926 timer_current = 0;
928 if(timer_initial != 0) // terminate the counting if timer_initial = 0
930 // This should trigger the counter to start. If already started,
931 // restart from the new start value.
932 BX_DEBUG(("APIC: Initial Timer Count Register = %u", value));
933 timer_current = timer_initial;
934 timer_active = 1;
935 Bit32u timervec = lvt[APIC_LVT_TIMER];
936 bx_bool continuous = (timervec & 0x20000) > 0;
937 ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
938 bx_pc_system.activate_timer_ticks(timer_handle,
939 Bit64u(timer_initial) * Bit64u(timer_divide_factor), continuous);
943 #if BX_SUPPORT_SAVE_RESTORE
944 void bx_local_apic_c::register_state(bx_param_c *parent)
946 unsigned i;
947 char name[6];
949 bx_list_c *lapic = new bx_list_c(parent, "local_apic", 25);
951 BXRS_HEX_PARAM_SIMPLE(lapic, base_addr);
952 BXRS_HEX_PARAM_SIMPLE(lapic, id);
953 BXRS_HEX_PARAM_SIMPLE(lapic, spurious_vector);
954 BXRS_PARAM_BOOL(lapic, software_enabled, software_enabled);
955 BXRS_PARAM_BOOL(lapic, focus_disable, focus_disable);
956 BXRS_HEX_PARAM_SIMPLE(lapic, task_priority);
957 BXRS_HEX_PARAM_SIMPLE(lapic, spurious_vector);
958 BXRS_HEX_PARAM_SIMPLE(lapic, log_dest);
959 BXRS_HEX_PARAM_SIMPLE(lapic, dest_format);
961 bx_list_c *ISR = new bx_list_c(lapic, "isr", BX_LAPIC_MAX_INTS);
962 bx_list_c *TMR = new bx_list_c(lapic, "tmr", BX_LAPIC_MAX_INTS);
963 bx_list_c *IRR = new bx_list_c(lapic, "irr", BX_LAPIC_MAX_INTS);
964 for (i=0; i<BX_LAPIC_MAX_INTS; i++) {
965 sprintf(name, "0x%02x", i);
966 new bx_shadow_num_c(ISR, name, &isr[i]);
967 new bx_shadow_num_c(TMR, name, &tmr[i]);
968 new bx_shadow_num_c(IRR, name, &irr[i]);
971 BXRS_HEX_PARAM_SIMPLE(lapic, error_status);
972 BXRS_HEX_PARAM_SIMPLE(lapic, shadow_error_status);
973 BXRS_HEX_PARAM_SIMPLE(lapic, icr_hi);
974 BXRS_HEX_PARAM_SIMPLE(lapic, icr_lo);
976 bx_list_c *LVT = new bx_list_c(lapic, "lvt", APIC_LVT_ENTRIES);
977 for (i=0; i<APIC_LVT_ENTRIES; i++) {
978 sprintf(name, "%d", i);
979 new bx_shadow_num_c(LVT, name, &lvt[i], BASE_HEX);
982 BXRS_HEX_PARAM_SIMPLE(lapic, timer_initial);
983 BXRS_HEX_PARAM_SIMPLE(lapic, timer_current);
984 BXRS_HEX_PARAM_SIMPLE(lapic, timer_divconf);
985 BXRS_DEC_PARAM_SIMPLE(lapic, timer_divide_factor);
986 BXRS_PARAM_BOOL(lapic, timer_active, timer_active);
987 BXRS_HEX_PARAM_SIMPLE(lapic, ticksInitial);
988 BXRS_PARAM_BOOL(lapic, INTR, INTR);
990 #endif
992 #endif /* if BX_SUPPORT_APIC */