mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 22:37:49 +00:00
Minor improvements
This commit is contained in:
@@ -6,7 +6,7 @@ date = 2018-07-26
|
||||
template = "second-edition/page.html"
|
||||
+++
|
||||
|
||||
In this post we set up the programmable interrupt controller to correctly forward hardware interrupts to the CPU. This allows us to create handler functions for these, which work in almost the same way as our exception handlers. We will then learn how to configure a hardware timer so that we get periodic interrupts and also how to add keyboard support.
|
||||
In this post we set up the programmable interrupt controller to correctly forward hardware interrupts to the CPU. To handle these interrups we add new entries to our interrupt descriptor table, just like we did for our exception handlers. We will learn how to get periodic timer interrupts and how to get input from the keyboard.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
@@ -21,6 +21,7 @@ Interrupts provide a way to notify the CPU from attached hardware devices. So in
|
||||
|
||||
[_polling_]: https://en.wikipedia.org/wiki/Polling_(computer_science)
|
||||
|
||||
Connecting all hardware devices directly to the CPU is not possible. Instead, a separate _interrupt controller_ aggregates the interrupts from all devices and then notifies the CPU:
|
||||
|
||||
```
|
||||
____________ _____
|
||||
@@ -31,9 +32,9 @@ Interrupts provide a way to notify the CPU from attached hardware devices. So in
|
||||
|
||||
```
|
||||
|
||||
Connecting all hardware devices directly to the CPU is not possible. Instead, a separate _interrupt controller_ aggregates the interrupts from all devices and then notifies the CPU. Most interrupt controllers are programmable, which means that they support different priority levels for interrupts. For example, we could give timer interrupts a higher priority than keyboard interrupts to ensure accurate timekeeping.
|
||||
Most interrupt controllers are programmable, which means that they support different priority levels for interrupts. For example, this allows to give timer interrupts a higher priority than keyboard interrupts to ensure accurate timekeeping.
|
||||
|
||||
Unlike exceptions, hardware interrupts occur asynchronously. This means that they are completely independent from the executed code and can occur at any time. Thus we suddenly have a form of concurrency in our kernel with all the potential concurrency-related bugs. Rust's strict ownership model helps us here because it forbids mutable global state. However, deadlocks are still possible, as we will see later in this post.
|
||||
Unlike exceptions, hardware interrupts occur _asynchronously_. This means that they are completely independent from the executed code and can occur at any time. Thus we suddenly have a form of concurrency in our kernel with all the potential concurrency-related bugs. Rust's strict ownership model helps us here because it forbids mutable global state. However, deadlocks are still possible, as we will see later in this post.
|
||||
|
||||
## The 8259 PIC
|
||||
|
||||
@@ -60,8 +61,9 @@ Secondary ATA ----> |____________| Parallel Port 1----> |____________|
|
||||
|
||||
This graphic shows the typical assignment of interrupt lines. We see that most of the 15 lines have a fixed mapping, e.g. line 4 of the slave PIC is assigned to the mouse.
|
||||
|
||||
Each controller can be configured through two I/O ports, one “command” port and one “data” port. For the master controller these ports are `0x20` (command) and `0x21` (data). For the slave they are `0xa0` (command) and `0xa1` (data). For more information on how the PICs can be configured see the [article on osdev.org].
|
||||
Each controller can be configured through two [I/O ports], one “command” port and one “data” port. For the master controller these ports are `0x20` (command) and `0x21` (data). For the slave they are `0xa0` (command) and `0xa1` (data). For more information on how the PICs can be configured see the [article on osdev.org].
|
||||
|
||||
[I/O ports]: ./second-edition/posts/05-integration-tests/index.md#port-i-o
|
||||
[article on osdev.org]: https://wiki.osdev.org/8259_PIC
|
||||
|
||||
### Implementation
|
||||
@@ -141,7 +143,7 @@ If all goes well we should continue to see the "It did not crash" message when e
|
||||
|
||||
## Enabling Interrupts
|
||||
|
||||
Until now nothing happened because we did not enable interrupts in the CPU. Let's change that:
|
||||
Until now nothing happened because interrupts are still disabled in the CPU configuration. This means that the CPU does not listen to the interrupt controller at all, so no interrupts can reach the CPU. Let's change that:
|
||||
|
||||
```rust
|
||||
// in src/main.rs
|
||||
@@ -161,7 +163,7 @@ pub extern "C" fn _start() -> ! {
|
||||
}
|
||||
```
|
||||
|
||||
This function of the `x86_64` crate executes the special `sti` instruction (“set interrupts”) to enable external interrupts. When we try `bootimage run` now, we see that a double fault occurs:
|
||||
The `interrupts::enable` function of the `x86_64` crate executes the special `sti` instruction (“set interrupts”) to enable external interrupts. When we try `bootimage run` now, we see that a double fault occurs:
|
||||
|
||||
TODO screenshot
|
||||
|
||||
@@ -176,7 +178,7 @@ As we see from the graphic [above](#the-8259-pic), the timer uses line 0 of the
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
pub const TIMER_INTERRUPT_ID: u8 = PIC_1_OFFSET;
|
||||
pub const TIMER_INTERRUPT_ID: u8 = PIC_1_OFFSET; // new
|
||||
|
||||
// in src/main.rs
|
||||
|
||||
@@ -204,7 +206,7 @@ We introduce a `TIMER_INTERRUPT_ID` constant to keep things organized. Our `time
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.2.11/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[`IndexMut`]: https://doc.rust-lang.org/core/ops/trait.IndexMut.html
|
||||
|
||||
In our timer interrupt handler, we print a dot to the screen. As the timer interrupt happens periodically, we expect to see a dot appearing on each timer tick. However, when we run it we see that only a single dot is printed:
|
||||
In our timer interrupt handler, we print a dot to the screen. As the timer interrupt happens periodically, we would expect to see a dot appearing on each timer tick. However, when we run it we see that only a single dot is printed:
|
||||
|
||||
TODO screenshot
|
||||
|
||||
@@ -225,7 +227,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(
|
||||
}
|
||||
```
|
||||
|
||||
The `notify_end_of_interrupt` figures out wether the master or slave PIC sent the interrupt and then uses the `command` and `data` registers to send an EOI signal to respective controllers. If the slave PIC sent the interrupt both PICs need to be notified because the slave PIC is connected to an input line of the master PIC.
|
||||
The `notify_end_of_interrupt` figures out wether the master or slave PIC sent the interrupt and then uses the `command` and `data` ports to send an EOI signal to respective controllers. If the slave PIC sent the interrupt both PICs need to be notified because the slave PIC is connected to an input line of the master PIC.
|
||||
|
||||
We need to be careful to use the correct interrupt vector number, otherwise we could accidentally delete an important unsent interrupt or cause our system to hang. This is the reason that the function is unsafe.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user