1 /////////////////////////////////////////////////////////////////////////
2 // $Id: apic.cc,v 1.96 2007/04/04 16:55:50 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2001 MandrakeSoft S.A.
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
31 #include "iodev/iodev.h"
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
)
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 !"));
55 return apic_bus_deliver_lowest_priority(vector
, dest
, trig_mode
, 0);
59 // determine destination local apics and deliver
61 if(dest
== APIC_BROADCAST_PHYSICAL_DESTINATION_MODE
)
63 return apic_bus_broadcast_interrupt(vector
, delivery_mode
, trig_mode
, APIC_MAX_ID
);
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
);
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
)
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
);
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();
117 int priority
= BX_CPU_APIC(i
)->get_tpr();
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
);
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
);
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
)
162 id
= APIC_UNKNOWN_ID
;
166 void bx_generic_apic_c::set_base(bx_phy_address 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
));
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
));
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
));
197 read_aligned(addr
& ~0x3, &value
);
198 if(len
== 4) { // must be 32-bit aligned
199 *((Bit32u
*)data
) = value
;
202 // handle partial read, independent of endian-ness
203 value
>>= (addr
&3)*8;
205 *((Bit8u
*) data
) = value
& 0xff;
207 *((Bit16u
*)data
) = value
& 0xffff;
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
));
218 bx_phy_address addr_aligned
= addr
& ~0x3;
219 if(len
== 4) { // must be 32-bit aligned
220 write_aligned(addr_aligned
, (Bit32u
*) data
);
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
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;
230 value
&= ~(0xff << shift
);
231 value
|= (*((Bit8u
*) data
) << shift
);
234 value
&= ~(0xffff << shift
);
235 value
|= (*((Bit16u
*)data
) << shift
);
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");
256 void bx_local_apic_c::reset()
258 /* same as INIT but also sets arbitration ID and APIC ID */
262 void bx_local_apic_c::init()
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;
281 for(i
=0; i
<BX_LAPIC_MAX_INTS
; i
++) {
282 irr
[i
] = isr
[i
] = tmr
[i
] = 0;
286 timer_divide_factor
= 1;
291 bx_pc_system
.deactivate_timer(timer_handle
);
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;
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
) {
310 sprintf(buffer
, "APIC%x", id
);
312 settype(CPU0LOG
+ id
);
313 sprintf(buffer
, "CPU%x", id
);
316 BX_INFO(("naming convention for apics requires id=0-%d only", APIC_MAX_ID
));
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);
330 Bit32u value
= *data
;
332 case 0x20: // local APIC id
333 id
= (value
>>24) & APIC_ID_MASK
;
335 case 0x80: // task priority
336 set_tpr(value
& 0xff);
341 case 0xd0: // logical destination
342 log_dest
= (value
>> 24) & APIC_ID_MASK
;
343 BX_DEBUG(("set logical destination to %02x", log_dest
));
345 case 0xe0: // destination format
346 dest_format
= (value
>> 28) & 0xf;
347 BX_DEBUG(("set destination format to %02x", dest_format
));
349 case 0xf0: // spurious interrupt vector
350 write_spurious_interrupt_register(value
);
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;
360 case 0x300: // interrupt command reg 0-31
361 icr_lo
= value
& ~(1<<12); // force delivery status bit = 0(idle)
364 case 0x310: // interrupt command reg 31-63
365 icr_hi
= value
& 0xff000000;
367 case 0x320: // LVT Timer Reg
368 lvt
[APIC_LVT_TIMER
] = value
& 0x300ff;
369 if(! software_enabled
) lvt
[APIC_LVT_TIMER
] |= 0x10000;
371 case 0x330: // LVT Thermal Monitor
372 lvt
[APIC_LVT_THERMAL
] = value
& 0x107ff;
373 if(! software_enabled
) lvt
[APIC_LVT_THERMAL
] |= 0x10000;
375 case 0x340: // LVT Performance Counter
376 lvt
[APIC_LVT_PERFORM
] = value
& 0x107ff;
377 if(! software_enabled
) lvt
[APIC_LVT_PERFORM
] |= 0x10000;
379 case 0x350: // LVT LINT0 Reg
380 lvt
[APIC_LVT_LINT0
] = value
& 0x1a7ff;
381 if(! software_enabled
) lvt
[APIC_LVT_LINT0
] |= 0x10000;
383 case 0x360: // LVT Lint1 Reg
384 lvt
[APIC_LVT_LINT1
] = value
& 0x1a7ff;
385 if(! software_enabled
) lvt
[APIC_LVT_LINT1
] |= 0x10000;
387 case 0x370: // LVT Error Reg
388 lvt
[APIC_LVT_ERROR
] = value
& 0x100ff;
389 if(! software_enabled
) lvt
[APIC_LVT_ERROR
] |= 0x10000;
391 case 0x380: // initial count for timer
392 set_initial_timer_count(value
);
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
);
399 /* all read-only registers go here */
400 case 0x30: // local APIC version
401 case 0x90: // arbitration priority
402 case 0xa0: // processor priority
404 case 0x100: case 0x110: case 0x120: case 0x130:
405 case 0x140: case 0x150: case 0x160: case 0x170:
407 case 0x180: case 0x190: case 0x1a0: case 0x1b0:
408 case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
410 case 0x200: case 0x210: case 0x220: case 0x230:
411 case 0x240: case 0x250: case 0x260: case 0x270:
412 // current count for timer
414 // all read-only registers should fall into this line
415 BX_INFO(("warning: write to read-only APIC register 0x%02x", addr
));
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);
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
);
452 trigger_irq(vector
, trig_mode
);
455 case 2: // all including self
456 accepted
= apic_bus_broadcast_interrupt(vector
, delivery_mode
, trig_mode
, APIC_MAX_ID
);
458 case 3: // all but self
459 accepted
= apic_bus_broadcast_interrupt(vector
, delivery_mode
, trig_mode
, get_id());
462 BX_PANIC(("Invalid desination shorthand %#x\n", dest_shorthand
));
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;
478 // bits 0-3 of the spurious vector hardwired to '1
479 spurious_vector
= (value
& 0xf0) | 0xf;
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
);
497 BX_DEBUG(("EOI written without any bit in ISR"));
499 if((Bit32u
) vec
!= spurious_vector
) {
500 BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu
->name
, vec
));
503 apic_bus_broadcast_eoi(vec
);
506 service_local_apic();
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
;
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
));
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;
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;
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.
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;
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
;
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
;
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
;
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;
622 case 0x380: // initial count for timer
623 *data
= timer_initial
;
625 case 0x390: // current count for timer
626 if(timer_active
==0) {
627 *data
= timer_current
;
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
;
639 case 0x3e0: // timer divide configuration
640 *data
= timer_divconf
;
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
;
657 void bx_local_apic_c::service_local_apic(void)
660 BX_INFO(("service_local_apic()"));
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
));
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
));
676 // interrupt has appeared in irr. Raise INTR. When the CPU
677 // acknowledges, we will run highest_priority_int again and
679 BX_DEBUG(("service_local_apic(): setting INTR=1 for vector 0x%02x", first_irr
));
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
) {
689 BX_DEBUG(("Deliver lowest priority of fixed interrupt vector %02x", vector
));
690 trigger_irq(vector
, trig_mode
);
693 BX_PANIC(("Delivery of SMI still not implemented !"));
697 BX_PANIC(("Delivery of NMI still not implemented !"));
701 BX_DEBUG(("Deliver INIT IPI"));
705 BX_DEBUG(("Deliver Start Up IPI"));
709 BX_DEBUG(("Deliver EXTINT vector %02x", vector
));
710 trigger_irq(vector
, trig_mode
, 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
));
729 BX_DEBUG(("triggered vector %#02x", vector
));
735 if(irr
[vector
] != 0) {
736 BX_DEBUG(("triggered vector %#02x not accepted", vector
));
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);
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
760 BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu
->name
));
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
));
770 BX_INFO(("Status after setting isr:"));
774 cpu
->async_event
= 1;
775 service_local_apic(); // will set INTR again if another is ready
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
]));
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"));
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)))
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();
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;
840 if(isrv
< 0) isrv
= 0;
841 if(irrv
< 0) irrv
= 0;
843 if((tpr
>= irrv
) && (tpr
> isrv
)) {
844 apr
= task_priority
& 0xff;
847 apr
= ((tpr
& isrv
) > irrv
) ?(tpr
& isrv
) : irrv
;
851 BX_DEBUG(("apr = %d\n", 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)
871 BX_ERROR(("%s: bx_local_apic_c::periodic called, timer_active==0", cpu
->name
));
875 // timer reached zero since the last call to periodic.
876 Bit32u timervec
= lvt
[APIC_LVT_TIMER
];
877 if(timervec
& 0x20000) {
879 // If timer is not masked, trigger interrupt.
880 if((timervec
& 0x10000)==0) {
881 trigger_irq(timervec
& 0xff, APIC_EDGE_TRIGGERED
);
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
));
894 // If timer is not masked, trigger interrupt.
895 if((timervec
& 0x10000)==0) {
896 trigger_irq(timervec
& 0xff, APIC_EDGE_TRIGGERED
);
899 BX_DEBUG(("%s: local apic timer LVT masked", cpu
->name
));
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.
921 bx_pc_system
.deactivate_timer(timer_handle
);
925 timer_initial
= value
;
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
;
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
)
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
);
992 #endif /* if BX_SUPPORT_APIC */