APIC Timer: Difference between revisions

1,588 bytes added ,  26 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
m (is)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(9 intermediate revisions by 6 users not shown)
Line 1:
The great benefit of the Local [[APIC]] timer is that it is hardwired to each CPU core, unlike the [[PIT|Programmable Interval Timer]] which is a separate circuit. Because of this, there is no need for any resource management, which makes things easier. The downside is that it's oscillating at (one of) the CPU's frequencies, which varies from machine to machine, while the PIT uses a standard frequency (11931821,193,182 Hz). To make use of it, you have to know how many interrupts/sec it's capable of.
 
== APIC Timer Modes ==
Line 26:
 
== Enabling APIC Timer ==
 
* First you have to enable the [[APIC#Local_APIC_configuration|Local APIC]] hardware by writing it's MSR.
Before enabling the local APIC timer, you should setup the rest of the local APIC. This includes:
* After that, you have to specify a spurious interrupt and software enable the APIC (this step is necessary).
 
* Finally, you specify APIC timer interrupt number and operation mode.
* Determine the local APIC's physical address (via. ACPI tables or MultiProcessor Specification tables)
You can find more detailed information in Intel manual vol3A Chapter 9.
* After that, you have to specifySpecify a spurious interrupt and software enable the APIC (this step is necessary).
* Make sure the TPR (Task Priority Register) is set (so it won't block/postpone lower priority IRQs)
 
Once that's done:
 
* Set the local APIC timer's divide configuration register
* Configure the local APIC timer's interrupt vector and unmask the timer's IRQ
* Set the local APIC timer's initial count
 
Note: It's recommended to follow the order given above (especially setting the local APIC timer's initial count last). Doing things in a different order (e.g. setting the initial count, then enabling the timer) can lead to problems on some (real or virtual) machines (e.g. everything seems right and counter is decreasing, but IRQ is never sent).
 
== Initializing ==
''Please note that this is the recommended way of determining the frequency of the APIC timer.''
There are several ways to do this, but all of them use a different, CPU bus frequency independent clock source to do that. Examples: [[RTC|Real Time Clock]], [[TSC|TimeStamp Counter]], PIT or even polling [[CMOS#Getting_Current_Date_and_Time_from_RTC|CMOS registers]]. In this tutorial we will use the good old PIT, as it's the easiest. Steps need to be done:
 
'''NOTE:''' According to Intel's documentation for IA-32 (x86) and Intel 64 (x86_64), APIC timer's frequency is equal to the bus' frequency '''OR''' the core crystal's frequency divided by the chosen frequency divider. The bus' and the core crystal's frequency can be found in the [[CPUID|CPUID]] functions [https://sandpile.org/x86/cpuid.htm#level_0000_0015h 0x15] and [https://sandpile.org/x86/cpuid.htm#level_0000_0016h 0x16], respectively. Through CPUID.0x15 can also be determined the TSC frequency. The frequency of the APIC timer depends on whether the system is using local APIC or ''discrete'' APIC ([https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller 82489DX]). When the local APIC is built in core's crystal, the APIC timer is using core's frequency. Otherwise, it is using the bus frequency.
 
 
There are several ways to do this, but all of them use a different, CPU bus frequency independent clock source to do that. Examples: [[RTC|Real Time Clock]], [[TSC|TimeStamp Counter]], PIT or even polling [[CMOS#Getting_Current_Date_and_Time_from_RTC|CMOS registers]]. In this tutorial we will use the good old PIT, as it's the easiest. Steps that need to be done:
* Reset APIC to a well known state
* Enable APIC timer
Line 42 ⟶ 57:
* Make the APIC timer fire an interrupt at every X ticks
 
The APIC timer can be set to make a tick (decrease counter) at a given frequency, which is called "divide value". This means you have to multiply APIC timer counter ticks by this divide value to get the true CPU bus frequency. You could use a value of 1 (ticks on every bus cycle) up to 128 (ticks on every 128th cycle). See Intel manual vol3A Chapter 9.5.4 onfor details. Note that according to my tests, Bochs seems not to handle divide value of 1 properly, so I will use 16.
 
=== Prerequisites ===
Before we start, let's define some constant and functions.
<sourcesyntaxhighlight lang="asm">
apic = the linear address where you have mapped the APIC registers
 
Line 83 ⟶ 99:
writegate: ...
ret
</syntaxhighlight>
</source>
I will also assume that you have a working [[IDT]], and you have a function to write a gate for a specific interrupt: writegate(intnumber,israddress).
Furthermore, to make things simple, I'll assume that you did not changedchange the default interrupt mapping found in almost every tutorial:
* interrupt 0-31: exceptions
* interrupt 32: timer, IRQ0
Line 94 ⟶ 110:
=== Example code in ASM ===
Here's a possible way to initialize APIC timer in fasm syntax assembly:
<sourcesyntaxhighlight lang="asm">
;you should read MSR, get APIC base and map to "apic"
;you should have used lidt properly
Line 198 ⟶ 214:
;although I have found buggy hardware that required it
mov dword [apic+APIC_TMRDIV], 03h
</syntaxhighlight>
</source>
 
=== Example code in C ===
This code is an example of how to initialize the APIC timer so that it ticks every 10 milliseconds. This is done by letting the APIC timer run, waiting for 10ms using the PIT and then getting the number of ticks that were done from the APIC timer. It assumes that you have functions to "write"/"read" the APIC's registers and "pit_prepare_sleep"/"pit_perform_sleep" to perform an as accurate as possible measuringmeasurement of the timerstimer's frequency.
<sourcesyntaxhighlight lang="c">
void apic_start_timer() {
// Tell APIC timer to use divider 16
Line 227 ⟶ 243:
write(APIC_REGISTER_TIMER_INITCNT, ticksIn10ms);
}
</syntaxhighlight>
</source>
 
== See also ==
Line 237 ⟶ 253:
=== External Links ===
* [http://www.intel.com/products/processor/manuals/ Volume 3A:System Programming Guide, Part 1,manuals has a chapter on the APIC]
* [http://www.osdever.net/tutorials/pdfview/apic.pdfadvanced-programming-interrupt-controller Advanced Programmable Interrupt Controller by Mike Rieker]
 
[[Category:Interrupts]]
[[Category:TimeTimers]]