1 /////////////////////////////////////////////////////////////////////////
2 // $Id: apic.cc,v 1.112 2008/12/01 19:06:14 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (c) 2002 Zwane Mwaikambo, Stanislav Shwartsman
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
26 #include "iodev/iodev.h"
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
)
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 !"));
50 return apic_bus_deliver_lowest_priority(vector
, dest
, trig_mode
, 0);
54 // determine destination local apics and deliver
56 if(dest
== APIC_BROADCAST_PHYSICAL_DESTINATION_MODE
)
58 return apic_bus_broadcast_interrupt(vector
, delivery_mode
, trig_mode
, APIC_MAX_ID
);
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
);
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
)
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
);
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();
112 int priority
= BX_CPU_APIC(i
)->get_tpr();
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
);
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
);
146 static void apic_bus_broadcast_eoi(Bit8u vector
)
148 bx_devices
.ioapic
->receive_eoi(vector
);
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();
167 ////////////////////////////////////
169 bx_generic_apic_c::bx_generic_apic_c(bx_phy_address base
)
173 id
= APIC_UNKNOWN_ID
;
177 void bx_generic_apic_c::set_base(bx_phy_address 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
));
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
));
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
));
208 read_aligned(addr
& ~0x3, &value
);
209 if(len
== 4) { // must be 32-bit aligned
210 *((Bit32u
*)data
) = value
;
213 // handle partial read, independent of endian-ness
214 value
>>= (addr
&3)*8;
216 *((Bit8u
*) data
) = value
& 0xff;
218 *((Bit16u
*)data
) = value
& 0xffff;
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
));
229 bx_phy_address addr_aligned
= addr
& ~0x3;
230 if(len
== 4) { // must be 32-bit aligned
231 write_aligned(addr_aligned
, (Bit32u
*) data
);
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
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;
241 value
&= ~(0xff << shift
);
242 value
|= (*((Bit8u
*) data
) << shift
);
245 value
&= ~(0xffff << shift
);
246 value
|= (*((Bit16u
*)data
) << shift
);
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");
262 reset(BX_RESET_HARDWARE
);
267 void bx_local_apic_c::reset(unsigned type
)
271 /* same as INIT but also sets arbitration ID and APIC ID */
275 void bx_local_apic_c::init()
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;
293 for(i
=0; i
<BX_LAPIC_MAX_INTS
; i
++) {
294 irr
[i
] = isr
[i
] = tmr
[i
] = 0;
298 timer_divide_factor
= 1;
303 bx_pc_system
.deactivate_timer(timer_handle
);
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;
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
) {
322 sprintf(buffer
, "APIC%x", id
);
324 settype(CPU0LOG
+ id
);
325 sprintf(buffer
, "CPU%x", id
);
328 BX_INFO(("naming convention for apics requires id=0-%d only", APIC_MAX_ID
));
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
;
344 case 0x20: // local APIC id
345 id
= (value
>>24) & APIC_ID_MASK
;
347 case 0x80: // task priority
348 set_tpr(value
& 0xff);
353 case 0xd0: // logical destination
354 log_dest
= (value
>> 24) & APIC_ID_MASK
;
355 BX_DEBUG(("set logical destination to %02x", log_dest
));
357 case 0xe0: // destination format
358 dest_format
= (value
>> 28) & 0xf;
359 BX_DEBUG(("set destination format to %02x", dest_format
));
361 case 0xf0: // spurious interrupt vector
362 write_spurious_interrupt_register(value
);
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;
372 case 0x300: // interrupt command reg 0-31
373 icr_lo
= value
& ~(1<<12); // force delivery status bit = 0(idle)
376 case 0x310: // interrupt command reg 31-63
377 icr_hi
= value
& 0xff000000;
379 case 0x320: // LVT Timer Reg
380 lvt
[APIC_LVT_TIMER
] = value
& 0x300ff;
381 if(! software_enabled
) lvt
[APIC_LVT_TIMER
] |= 0x10000;
383 case 0x330: // LVT Thermal Monitor
384 lvt
[APIC_LVT_THERMAL
] = value
& 0x107ff;
385 if(! software_enabled
) lvt
[APIC_LVT_THERMAL
] |= 0x10000;
387 case 0x340: // LVT Performance Counter
388 lvt
[APIC_LVT_PERFORM
] = value
& 0x107ff;
389 if(! software_enabled
) lvt
[APIC_LVT_PERFORM
] |= 0x10000;
391 case 0x350: // LVT LINT0 Reg
392 lvt
[APIC_LVT_LINT0
] = value
& 0x1a7ff;
393 if(! software_enabled
) lvt
[APIC_LVT_LINT0
] |= 0x10000;
395 case 0x360: // LVT Lint1 Reg
396 lvt
[APIC_LVT_LINT1
] = value
& 0x1a7ff;
397 if(! software_enabled
) lvt
[APIC_LVT_LINT1
] |= 0x10000;
399 case 0x370: // LVT Error Reg
400 lvt
[APIC_LVT_ERROR
] = value
& 0x100ff;
401 if(! software_enabled
) lvt
[APIC_LVT_ERROR
] |= 0x10000;
403 case 0x380: // initial count for timer
404 set_initial_timer_count(value
);
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
);
411 /* all read-only registers go here */
412 case 0x30: // local APIC version
413 case 0x90: // arbitration priority
414 case 0xa0: // processor priority
416 case 0x100: case 0x110: case 0x120: case 0x130:
417 case 0x140: case 0x150: case 0x160: case 0x170:
419 case 0x180: case 0x190: case 0x1a0: case 0x1b0:
420 case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
422 case 0x200: case 0x210: case 0x220: case 0x230:
423 case 0x240: case 0x250: case 0x260: case 0x270:
424 // current count for timer
426 // all read-only registers should fall into this line
427 BX_INFO(("warning: write to read-only APIC register 0x%x", apic_reg
));
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);
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
);
464 trigger_irq(vector
, trig_mode
);
467 case 2: // all including self
468 accepted
= apic_bus_broadcast_interrupt(vector
, delivery_mode
, trig_mode
, APIC_MAX_ID
);
470 case 3: // all but self
471 accepted
= apic_bus_broadcast_interrupt(vector
, delivery_mode
, trig_mode
, get_id());
474 BX_PANIC(("Invalid desination shorthand %#x\n", dest_shorthand
));
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;
490 // bits 0-3 of the spurious vector hardwired to '1
491 spurious_vector
= (value
& 0xf0) | 0xf;
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
);
509 BX_DEBUG(("EOI written without any bit in ISR"));
511 if((Bit32u
) vec
!= spurious_vector
) {
512 BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu
->name
, vec
));
515 apic_bus_broadcast_eoi(vec
);
518 service_local_apic();
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()));
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;
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;
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.
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;
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
;
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
;
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
;
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;
635 case 0x380: // initial count for timer
636 *data
= timer_initial
;
638 case 0x390: // current count for timer
639 if(timer_active
==0) {
640 *data
= timer_current
;
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
;
650 case 0x3e0: // timer divide configuration
651 *data
= timer_divconf
;
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
;
668 void bx_local_apic_c::service_local_apic(void)
671 BX_INFO(("service_local_apic()"));
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
));
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
));
687 // interrupt has appeared in irr. Raise INTR. When the CPU
688 // acknowledges, we will run highest_priority_int again and
690 BX_DEBUG(("service_local_apic(): setting INTR=1 for vector 0x%02x", first_irr
));
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
) {
700 BX_DEBUG(("Deliver lowest priority of fixed interrupt vector %02x", vector
));
701 trigger_irq(vector
, trig_mode
);
704 BX_INFO(("Deliver SMI"));
708 BX_INFO(("Deliver NMI"));
712 BX_INFO(("Deliver INIT IPI"));
716 BX_DEBUG(("Deliver Start Up IPI"));
720 BX_DEBUG(("Deliver EXTINT vector %02x", vector
));
721 trigger_irq(vector
, trig_mode
, 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
));
740 BX_DEBUG(("triggered vector %#02x", vector
));
746 if(irr
[vector
] != 0) {
747 BX_DEBUG(("triggered vector %#02x not accepted", vector
));
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);
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
771 BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu
->name
));
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
));
781 BX_INFO(("Status after setting isr:"));
785 cpu
->async_event
= 1;
786 service_local_apic(); // will set INTR again if another is ready
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
]));
806 bx_bool
bx_local_apic_c::match_logical_addr(Bit8u address
)
810 if (dest_format
== 0xf) {
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) {
818 if (address
== 0xff) // broadcast all
821 if ((unsigned)(address
& 0xf0) == (unsigned)(log_dest
& 0xf0))
822 match
= ((address
& log_dest
& 0x0f) != 0);
825 BX_PANIC(("bx_local_apic_c::match_logical_addr: unsupported dest format 0x%x", dest_format
));
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)))
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();
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;
869 if((tpr
>= irrv
) && (tpr
> isrv
)) {
870 apr
= task_priority
& 0xff;
873 apr
= ((tpr
& isrv
) > irrv
) ?(tpr
& isrv
) : irrv
;
877 BX_DEBUG(("apr = %d\n", 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)
897 BX_ERROR(("%s: bx_local_apic_c::periodic called, timer_active==0", cpu
->name
));
901 // timer reached zero since the last call to periodic.
902 Bit32u timervec
= lvt
[APIC_LVT_TIMER
];
903 if(timervec
& 0x20000) {
905 // If timer is not masked, trigger interrupt.
906 if((timervec
& 0x10000)==0) {
907 trigger_irq(timervec
& 0xff, APIC_EDGE_TRIGGERED
);
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
));
920 // If timer is not masked, trigger interrupt.
921 if((timervec
& 0x10000)==0) {
922 trigger_irq(timervec
& 0xff, APIC_EDGE_TRIGGERED
);
925 BX_DEBUG(("%s: local apic timer LVT masked", cpu
->name
));
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.
947 bx_pc_system
.deactivate_timer(timer_handle
);
951 timer_initial
= value
;
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
;
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
)
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 */