Debugging the PIT in Bochs

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Debugging the PIT in Bochs

Post by AJ »

Hi All,

I have re-written large portions of my OS code to take in to account a more flexible approach to drivers. One of the things I have just done is re-written my PIT initialisation code, which is pretty simple, as follows:

Code: Select all

void kt_install()
{
    kt_pit_frequency(1000);
    ki_irq_enable(0);
}

void kt_pit_frequency(uint32 hz)
{
    int div = 1193180 / hz;
    outportb(0x43, 0x36);
    outportb(0x40, div&0xFF);
    outportb(0x40, (div>>8)&0xFF);
}
Doesn't look like it could get much simpler! It's pretty much lifted from Bran's kernel development tutorial. The reason for 3 functions is that kt_install() ('kernel time') will eventually encompass the RTC clock too. The irq enable function simply un-masks IRQ0.

The only problem is that in Bochs, I constantly get the message '[PIT81] Undefined behaviour when loading a half loaded count'. The IRQ also only fires once, despite writing an EOI to the PIC.

What really simple thing have I missed here - I had a working PIT in my old version, but fail to see the difference between my old and new code. It's almost embarrasing! :oops:

Oh - BTW, the interrupt handler for the PIT currently just does the EOI and an iret - I'm not even bothering to increment a tick counter until I get the simple code right!

Thanks in advance,
Adam
User avatar
Happy Egghead
Member
Member
Posts: 32
Joined: Thu Oct 19, 2006 5:25 am
Location: Adelaide, Australia
Contact:

Post by Happy Egghead »

Hi,
I've just written my PIT code and it's seems to be working OK. Straight off the bat I can recommend an optimization and that is in the second setting of your divisor to the PIT the (&0xFF) isn't needed after shifting right (>>).

My PIT code for comparison is as follows

Code: Select all


#define TIMER_CONTROL_PORT	0x43
#define TIMER_DATA_PORT		   0x40
#define SYSTEM_TIMER_SETUP	 0x36

void set_system_timer_speed(uint_t hz)
{
	uint_t divisor;
	
    divisor = 1193180 / hz;       // Calculate our divisor
	
	if (!divisor) divisor++;
	
    system_timer_speed = hz;
	
    outb(TIMER_CONTROL_PORT, SYSTEM_TIMER_SETUP);             // Set our command byte 0x36 - 00 11 010 0b
    outb(TIMER_DATA_PORT, divisor & 0xFF);   // Set low byte of divisor
    outb(TIMER_DATA_PORT, divisor >> 8);     // Set high byte of divisor
}
The only other thing I can think of is that something is masking the irq without unmasking it later. I was trying to add spinlocks to various global variables and everything ground to a confusing halt.

Another problem encountered was that there was code disabling interrupts and not re-enabling them. Again - no interrupts - no timer tick. I eventually found that in my task switch function there was a possiblity of the EOI not being sent at all, I had to move it higher up in the function to guarantee that regardless of why the interrupt was called the EOI would be sent.
Does the EOI to the PIT occur in assembler or C? I remember in another post your timer was optimised to minimise calling the reschedule function (in C). I think there's some way to ask Bochs to log whether or not the EOI was actually received. Something in the log file settings.

Hope this helps somewhat!
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Happy Egghead wrote:I've just written my PIT code and it's seems to be working OK. Straight off the bat I can recommend an optimization and that is in the second setting of your divisor to the PIT the (&0xFF) isn't needed after shifting right (>>).
Thanks for this. Will do!
Happy Egghead wrote: The only other thing I can think of is that something is masking the irq without unmasking it later. I was trying to add spinlocks to various global variables and everything ground to a confusing halt.
Thanks for the thought, but the problem only occurs when I unmask the interrupt. If it is masked (ie, not working), Bochs does not seem to give me the warning. Also, if i CLI, no warning. Looks like the problem only hapens when the PIT is allowed to fire.
Happy Egghead wrote: Does the EOI to the PIT occur in assembler or C? I remember in another post your timer was optimised to minimise calling the reschedule function (in C). I think there's some way to ask Bochs to log whether or not the EOI was actually received. Something in the log file settings.
Well remembered! That is certainly the case, but as I am rewriting such a large portion of the code, I have not put my multitasking routines back again yet. The EOI is sent in C, again, as per Brandon's kernel development tutorial(ish):

Code: Select all

printf("Unhandled IRQ %#x (%d) - device driver not installed!\n", r->errcode, r->errcode);
/*  send 'end of interrupt' commands to PIC */
if(r->errcode>=0x08) outportb(PIC_SLAVE, PIC_EOI);
outportb(PIC_MASTER, PIC_EOI);
The difference between my code and Brandon's is that a single C handler handles all interrupts (Exceptions, IRQ's, Software ints). The idea is to optimise this later. The 'errcode' field contains the IRQ. I know this works, as the first time the timer fires, I get the message on the screen as expected from the code above. The function then returns to the ASM stub and iret's.

After timer initialisation, my kernel just goes in to an idle loop at the moment. I'm not handling any other IRQ yet, so don't think it's a masking issue. Before the PIT initialisation functions (above), I don't touch the PIT.

I'm just going to write a simpler ASM handler and see how I get on, but my feeling is still that the PIT is to blame, not my handler code, because bochs prefixes the message with [PIT81]....

Thanks for your help,
Adam[/code]
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

B*gger! Looks like the same happens when I disable the timer but enable the keyboard IRQ. Oh well, back to the drawing board!

Any ideas why whenever the first IRQ fires, bochs puts out an error on [PIT81]? As before, the exact wording is:

'[PIT81] Undefined behaviour when loading a half loaded count'.

This error is then repeated over and over on the Bochs console. If it's not to do with the PIT, this message seems completely useless. Of course, if it is helpful to post any sample code with this request for help, I will do so!

Cheers,
Adam
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

B*gger again. Sorry - bad hair day. I've *only* figured out what was wrong - and it's a case of bad naming of defines.

In my sample above, I used the code:

Code: Select all

if(r->errcode>=0x08) outportb(PIC_SLAVE, PIC_EOI); 
outportb(PIC_MASTER, PIC_EOI); 
PIC_SLAVE and PIC_MASTER actually point to 0x40 and 0x48 - the region I am mapping my PIC's to. I should have used:

Code: Select all

if(r->errcode>=0x08) outportb(PIC_SLAVE_CMD, PIC_EOI); 
outportb(PIC_MASTER_CMD, PIC_EOI); 
Which sends the EOI to the PIC command ports! Of course, it was reported as a PIT error because port 0x40 is PIT channel 0.

I humbly apologise to anyone who was scratching their heads over this infuriating question. :roll:

Adam
User avatar
Happy Egghead
Member
Member
Posts: 32
Joined: Thu Oct 19, 2006 5:25 am
Location: Adelaide, Australia
Contact:

Post by Happy Egghead »

Great to see you found the problem! The hair loss is hereditary. :)

I found it easier to separate all of my interrupts / exceptions from each other. From what I see in Brandons tutorial, hardware interrupts are routed through a table while exceptions are routed to a single fault handler.
My code uses the same for exceptions (only one of them is expected at a time and are dealt with by a switch/case) but I went for device specific interrupts.
I found it's faster (no redirection). The address for the interrupt would be stored right in the IDT using that IDT_set_gate function. It's also possible to output debugging information from each interrupt separate from what Bochs is putting out. If an IRQ fires, it's not just a block of common code being called - it's the devices actual IRQ going off.
With this setup it is up to the interrupt code of the device to ACK the interrupt not the generic handler. A spurious_master and spurious_slave handler just increments a counter and exits.
No interrupts and a whole lot of spurioussness points to uninstalled interrupt problems rapidly.
Speed v's compactness tradeoff. As long as it all works!

Lara Bingle would have a field day with this -
"Interrupts - Where the bloody hell are you?"
Post Reply