Hi,
Just some notes...
Mark wrote:Get local APIC timer setup and generating interrupts (have to use irq > 32)
For older single-CPU computers (before Pentium 4) the local APIC is normally disabled by the BIOS during boot, and can't be re-enabled. For these computers use the PIT timer, which is easier. Depending on whether you'll support these older computers or not I'd suggest making the OS work with the PIT and worry about the local APIC timer later.
Mark wrote:set up idt to handle irq - I decided to use a task gate.
For most interrupts, using a task gate is slow, and it doesn't work for shared IRQs (PCI, serial ports) where the IRQ handlers need to be chained. Interrupt task gates aren't normally needed, except for a few exception handlers (e.g. double fault), but even then it's optional (easier to write the OS so that doesn't need interrupt task gates at all).
For the timer IRQ, use an "interrupt gate" and then (if a task switch is needed) use a far jump to switch tasks. This saves task switches - if you use an "interrupt task gate" then the CPU would always do one task switch to get to the timer IRQ handler and then another task switch to return (2 task switches every IRQ). Using an "interrupt gate" means you end up with only one task switch, and only when it's necessary.
Often there's only one task that is ready to run (no task switch needed), and most scheduler designs do a task switch after many IRQs - eg. set the timer for an IRQ every 1 mS and switch tasks after 20 mS. This is normally done when the same timer IRQ is used to maintain the "system timer tick". For example:
Code: Select all
timerIRQ:
current_time += time_between_timer_IRQs;
send_timer_EOI();
if (next_task_switch_time < current_time) {
new_task = find_another_task_to_run();
next_task_switch_time = current_time + time_this_task_runs(new_task);
if (new_task != current_task) {
switch_tasks();
}
}
iret
Of course this isn't the only way to the timing (but IMHO it is the most common)...
Cheers,
Brendan