StuBS
|
StuBS needs simple interrupt handling, e.g. for the keyboard. To configure the interrupts, StuBS will support the Advanced Programmable Interrupt Controller.
A driver for an interrupt-driven device like the Keyboard inherits from Gate, defining the interface. The Plugbox stores the mapping from interrupt vectors to Gate objects and gets queried by the interrupt_handler during interrupt handling.
However, to allow interrupts from external devices you have to configure them in the IOAPIC during initialization. The low level part of the interrupt_handler, including the entry functions, setting up the IDT and LAPIC, as well as helper functions (e.g., to enable/disable interrupts on the current core), are already implemented.
Modern PCs usually support xAPIC, having up to 24 external devices connected to the I/O APIC (you can even have multiple I/O APICs – although StuBS only supports the first one), with a local APIC integrated in each core and all connected via the APIC bus.
During the boot process, interrupts are masked (i.e., disabled) for each core and the I/O APIC. Implement the functions in IOAPIC to initialize the IOAPIC and configure interrupts from external sources. To test your implementation, you can configure (high level triggered: IOAPIC::HIGH & IOAPIC::LEVEL) and enable interrupts from the keyboard (using APIC::getIOAPICSlot with APIC::KEYBOARD). The interrupt vector table has already been initialized by the startup code, so that the interrupt_handler() should C-function will be executed with appropriate arguments when an interrupt, like a key press or release, occurs! However, since the characters are not fetched by the PS2Controller, you will continuously receive the interrupt until you empty the keyboard buffer by reading its content using either PS2Controller::fetch() or PS2Controller::drainBuffer().
Depending on the environment, it may also be necessary to empty the keyboard buffer completely before reactivating interrupts.
iret
[interrupt return]).Now you need to make sure that each device driver's interrupt service routine is called once the corresponding interrupt fires. The Plugbox should be used to manage those driver objects, providing a pointer to a Gate object for each registered interrupt vector. Gate is an abstract class that describes the interface of all interrupt handling drivers.
Initially, all indices (vectors) of the Plugbox should point to a (global) device driver object which – as the name implies – should cause the kernel to panic if an unknown interrupt fires.
Write the Keyboard device driver which implements the Gate interface and should be registered to handle the interrupts triggered by the keyboard. For now, after each keystroke, the corresponding characters should be displayed on the screen. Allocate one line of your screen to display these characters. The key combination Ctrl-Alt-Delete should trigger a system reboot.
You can reuse the PS2Controller implementation of assignment 1 to query for the Key, however, you have to adjust the function PS2Controller::fetch(Key&) to the changed situation.
Adapt your test program to the interrupt processing.
Implement a test program in Application::action(), which is called from main(). This should infinitely generate output (for example, an increasing number) at a fixed position of the main output window kout
(using TextStream::setPos()).
With the keyboard driver implemented as described above, pressing keys will jumble the application's output. Think about what is happening, why it is happening, and avoid the problem using functions provided by the Core namespace (or its subnamespaces) – while still using the kout
object for the Keyboard device.
Implement a test program in Application::action() similar to OOStuBS, but create an instance for each core, which is then called in main() and main_ap() respectively. The output of each instance should be in a distinct but fixed line in kout
. On the first execution, you'll see that the multi-core system does not even need interrupts to mess up the output (Think about why that is the case). To avoid this problem, the methods provided by Core alone are not enough. Therefore, you have to write a Spinlock (or Ticketlock) in order to synchronize parallel control flows. Think about what must be considered when using locks and what problems can occur.
Further Reading: Mutual exclusion (mutex)