APIC timer
APIC timer
Hi
I am trying to configure APIC timer.
But it doesn’t work.
My interruption handler isn't called.
After booting via GRUB I do next steps:
1. fill IDT
2. initialize APIC
- enable APIC in SVR (0x0F0)
- set flat mode in DEST (0x0E0)
3. init timer
- set timer divide (0x3E0) to (old_value | 0xb)
- set periodic mode and vector in timer LVT (0x320)
vector 0x40
- set init count (0x380) to 100
- set IDT entry 0x40 with timer_handler address
and nothing happened.
I do it in QEMU!
Do I miss something?
Al.
I am trying to configure APIC timer.
But it doesn’t work.
My interruption handler isn't called.
After booting via GRUB I do next steps:
1. fill IDT
2. initialize APIC
- enable APIC in SVR (0x0F0)
- set flat mode in DEST (0x0E0)
3. init timer
- set timer divide (0x3E0) to (old_value | 0xb)
- set periodic mode and vector in timer LVT (0x320)
vector 0x40
- set init count (0x380) to 100
- set IDT entry 0x40 with timer_handler address
and nothing happened.
I do it in QEMU!
Do I miss something?
Al.
Re: APIC timer
Hi,
0x00000100 -> 0xFFE001FF - enable local APIC and set spurious int vector to 0xFF
0xFFFFFFFF -> 0xFEE000E0 - set destination format register to flat model
0x00000000 -> 0xFEE00080 - set the task priority register to accept all interrupts
0x0000000B -> 0xFEE003E0 - set timer to divide by 1
0x00020040 -> 0xFEE00320 - enable timer, set periodic mode, set int vector to 0x40
0x00000064 -> 0xFEE00380 - set initial count to 100 (and make the timer start counting)
If you're setting the timer counter to divide by one and using an initial count of 100, then it'd work out to a delay of between 4 uS (25 MHz bus) and 100 nS (1 GHz bus). This seems insanely fast to me...
You don't need to read the old value and then change it - it's safe to assume all reserved bits are zero (or one for the destination format register).
Also, for some Pentium CPUs (IIRC family 5, model 2, stepping 0x0B) there was a bug with the local APIC, where you need to do a dummy read before doing a write.
Of course before you do anything with the local APIC you should find out if the base address is 0xFEE00000 or something else, and all of your local APIC code should be written so that it works if the local APIC isn't at 0xFEE00000.
Apart from that, I'm out of ideas (except for obvious things, like CLI without STI, etc).
Cheers,
Brendan
It should go something like:Al wrote:Do I miss something?
0x00000100 -> 0xFFE001FF - enable local APIC and set spurious int vector to 0xFF
0xFFFFFFFF -> 0xFEE000E0 - set destination format register to flat model
0x00000000 -> 0xFEE00080 - set the task priority register to accept all interrupts
0x0000000B -> 0xFEE003E0 - set timer to divide by 1
0x00020040 -> 0xFEE00320 - enable timer, set periodic mode, set int vector to 0x40
0x00000064 -> 0xFEE00380 - set initial count to 100 (and make the timer start counting)
If you're setting the timer counter to divide by one and using an initial count of 100, then it'd work out to a delay of between 4 uS (25 MHz bus) and 100 nS (1 GHz bus). This seems insanely fast to me...
You don't need to read the old value and then change it - it's safe to assume all reserved bits are zero (or one for the destination format register).
Also, for some Pentium CPUs (IIRC family 5, model 2, stepping 0x0B) there was a bug with the local APIC, where you need to do a dummy read before doing a write.
Of course before you do anything with the local APIC you should find out if the base address is 0xFEE00000 or something else, and all of your local APIC code should be written so that it works if the local APIC isn't at 0xFEE00000.
Apart from that, I'm out of ideas (except for obvious things, like CLI without STI, etc).
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: APIC timer
Thanks a lot for your response
But what do you mean?
But what do you mean?
Brendan wrote: Apart from that, I'm out of ideas (except for obvious things, like CLI without STI, etc).
We must use time as a tool, not as a crutch.
JFK.
JFK.
Re: APIC timer
Hi,
Other problems would include forgetting to call the routine that initialises the local APIC code (I do that sometimes), trying to use the local APIC when it isn't there (literally not present for 80486, permanently disabled for later CPUs, or simply not emulated by the emulator), having some sort of bug in the IRQ handler (e.g. doing "mov byte [0xB800],0x0741" and assuming the IRQ didn't happen because nothing changed on the screen). There's plenty of mistakes that are possible...
[edit] There is another problem I should've mentioned - you must use 32-bit reads and writes when you're working with the local APIC. Something like "mov byte [0xFEE003E0],0x0B" won't get to the local APIC because it's 8-bit instead of 32-bit. In this case the CPU sends the access out to the memory controller (which should forward it to the PCI bus, which probably ignores it). [/edit]
Cheers,
Brendan
If interrupts are disabled, the CPU won't invoke your interrupt handler (unless the interrupt is an NMI, which isn't possible for the local APIC timer).Al wrote:Thanks a lot for your response
But what do you mean?Brendan wrote: Apart from that, I'm out of ideas (except for obvious things, like CLI without STI, etc).
Other problems would include forgetting to call the routine that initialises the local APIC code (I do that sometimes), trying to use the local APIC when it isn't there (literally not present for 80486, permanently disabled for later CPUs, or simply not emulated by the emulator), having some sort of bug in the IRQ handler (e.g. doing "mov byte [0xB800],0x0741" and assuming the IRQ didn't happen because nothing changed on the screen). There's plenty of mistakes that are possible...
[edit] There is another problem I should've mentioned - you must use 32-bit reads and writes when you're working with the local APIC. Something like "mov byte [0xFEE003E0],0x0B" won't get to the local APIC because it's 8-bit instead of 32-bit. In this case the CPU sends the access out to the memory controller (which should forward it to the PCI bus, which probably ignores it). [/edit]
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: APIC timer
one more Q
But I found that 0xFFE001FF is in the read only space
What register is situated in 0xFFE001FF?
0xFEE000F0H - Spurious Interrupt Vector RegisterBrendan wrote: It should go something like:
0x00000100 -> 0xFFE001FF - enable local APIC and set spurious int vector to 0xFF
But I found that 0xFFE001FF is in the read only space
What register is situated in 0xFFE001FF?
We must use time as a tool, not as a crutch.
JFK.
JFK.
Re: APIC timer
Hi,
0x000001FF -> 0xFFE000F0 - enable local APIC and set spurious int vector to 0xFF
This isn't necessarily a good idea because using int vector 0xFF makes it one of the highest priority interrupts. A better idea might be to use int vector 0x2F - the lowest priority interrupt that is possible for the spurious interrupt, considering that the first 32 interrupts are reserved for exceptions and that the lowest 3 bits are hardwired to '1' for Pentium and P6 CPUs.
Cheers,
Brendan
That was a typo! It should have been:Al wrote:0xFEE000F0H - Spurious Interrupt Vector RegisterBrendan wrote: It should go something like:
0x00000100 -> 0xFFE001FF - enable local APIC and set spurious int vector to 0xFF
But I found that 0xFFE001FF is in the read only space
What register is situated in 0xFFE001FF?
0x000001FF -> 0xFFE000F0 - enable local APIC and set spurious int vector to 0xFF
This isn't necessarily a good idea because using int vector 0xFF makes it one of the highest priority interrupts. A better idea might be to use int vector 0x2F - the lowest priority interrupt that is possible for the spurious interrupt, considering that the first 32 interrupts are reserved for exceptions and that the lowest 3 bits are hardwired to '1' for Pentium and P6 CPUs.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: APIC timer
Thx, now it works, but only once.
I've found that EOI Register (0XFEE000B0) should be updated with zero on every handling. I didn't understand complitly why, but anyway it doesn't help.
Al.
I've found that EOI Register (0XFEE000B0) should be updated with zero on every handling. I didn't understand complitly why, but anyway it doesn't help.
Al.
We must use time as a tool, not as a crutch.
JFK.
JFK.
Re: APIC timer
Hi,
For "one-shot" mode you'd only get one interrupt, and then you'd have to send the EOI and set the initial count register again before you get another interrupt.
I'd check the code sets the timer mode correctly, check that the EOI is being sent correctly, and then perhaps try it on a slower frequency (I'm not sure what happens when the timer generates interrupts faster than the CPU can handle). Apart from that I'm lost - it should work...
Cheers,
Brendan
Hmm - for periodic mode, sending the EOI is all you should need.Al wrote:Thx, now it works, but only once.
I've found that EOI Register (0XFEE000B0) should be updated with zero on every handling. I didn't understand complitly why, but anyway it doesn't help.
For "one-shot" mode you'd only get one interrupt, and then you'd have to send the EOI and set the initial count register again before you get another interrupt.
I'd check the code sets the timer mode correctly, check that the EOI is being sent correctly, and then perhaps try it on a slower frequency (I'm not sure what happens when the timer generates interrupts faster than the CPU can handle). Apart from that I'm lost - it should work...
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: APIC timer
Am I right that "send EIO" just
0x00000000 -> 0xFEE000B0?
0x00000000 -> 0xFEE000B0?
We must use time as a tool, not as a crutch.
JFK.
JFK.
Re: APIC timer
Hi,
Of course the local APIC timer is often used by the scheduler for pre-empting threads. In this case the EOI must happen before the scheduler does a task switch...
For those dodgy Pentiums, I'm not sure if it's faster to test if the dummy read is necessary, or to always do a dummy read (regardless of whether it's needed or not). IMHO it's likely that it's faster to always do the dummy read.
Depending on your OS, it's possibly better to wrap all local APIC writes in a macro:
That way you can skip the test and the dummy read under certain conditions - for e.g. if you have one version of the kernel for plain paging and another for PAE, then you know that the dummy read isn't necessary for the PAE version because Pentiums didn't support PAE anyway...
Cheers,
Brendan
Yes. For example:Al wrote:Am I right that "send EIO" just
0x00000000 -> 0xFEE000B0?
Code: Select all
test dword [gs:CPUdata.errata],CPUERRATAbadLAPIC
je .doWrite
cmp dword [local_APIC_base + 0x0030],eax ;Dummy read for dodgy Pentiums
.doWrite:
mov dword [local_APIC_base + 0x00B0],0
pop <stuff>
iret
Code: Select all
test dword [gs:CPUdata.errata],CPUERRATAbadLAPIC
je .doWrite
cmp dword [local_APIC_base + 0x0030],eax ;Dummy read for dodgy Pentiums
.doWrite:
mov dword [local_APIC_base + 0x00B0],0
call do_task_switch_thing
pop <stuff>
iret
Depending on your OS, it's possibly better to wrap all local APIC writes in a macro:
Code: Select all
%macro LAPICwrite 2
%ifdef ??????
cmp [local_APIC_base + 0x0030],eax ;Dummy read
%endif
mov [local_APIC_base + %1], %2
%endif
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.