You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: blog/content/edition-2/posts/04-testing/index.md
+3-3
Original file line number
Diff line number
Diff line change
@@ -174,18 +174,18 @@ The functionality of the `isa-debug-exit` device is very simple. When a `value`
174
174
175
175
Instead of manually invoking the `in` and `out` assembly instructions, we use the abstractions provided by the [`x86_64`] crate. To add a dependency on that crate, we add it to the `dependencies` section in our `Cargo.toml`:
176
176
177
-
[`x86_64`]: https://docs.rs/x86_64/0.13.2/x86_64/
177
+
[`x86_64`]: https://docs.rs/x86_64/0.14.2/x86_64/
178
178
179
179
```toml
180
180
# in Cargo.toml
181
181
182
182
[dependencies]
183
-
x86_64 = "0.13.2"
183
+
x86_64 = "0.14.2"
184
184
```
185
185
186
186
Now we can use the [`Port`] type provided by the crate to create an `exit_qemu` function:
The fields have the type [`idt::Entry<F>`], which is a struct that represents the fields of an IDT entry (see the table above). The type parameter `F` defines the expected handler function type. We see that some entries require a [`HandlerFunc`] and some entries require a [`HandlerFuncWithErrCode`]. The page fault even has its own special type: [`PageFaultHandlerFunc`].
It's a [type alias] for an `extern "x86-interrupt" fn` type. The `extern` keyword defines a function with a [foreign calling convention] and is often used to communicate with C code (`extern "C" fn`). But what is the `x86-interrupt` calling convention?
@@ -195,7 +195,7 @@ So the _interrupt stack frame_ looks like this:
195
195
196
196
In the `x86_64` crate, the interrupt stack frame is represented by the [`InterruptStackFrame`] struct. It is passed to interrupt handlers as `&mut` and can be used to retrieve additional information about the exception's cause. The struct contains no error code field, since only some few exceptions push an error code. These exceptions use the separate [`HandlerFuncWithErrCode`] function type, which has an additional `error_code` argument.
The `x86-interrupt` calling convention is a powerful abstraction that hides almost all of the messy details of the exception handling process. However, sometimes it's useful to know what's happening behind the curtain. Here is a short overview of the things that the `x86-interrupt` calling convention takes care of:
@@ -277,7 +277,7 @@ This error occurs because the `x86-interrupt` calling convention is still unstab
277
277
In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `InterruptDescriptorTable` struct of the `x86_64` provides a [`load`][InterruptDescriptorTable::load] method function for that. Let's try to use it:
The `x86-interrupt` calling convention and the [`InterruptDescriptorTable`] type made the exception handling process relatively straightforward and painless. If this was too much magic for you and you like to learn all the gory details of exception handling, we got you covered: Our [“Handling Exceptions with Naked Functions”] series shows how to handle exceptions without the `x86-interrupt` calling convention and also creates its own IDT type. Historically, these posts were the main exception handling posts before the `x86-interrupt` calling convention and the `x86_64` crate existed. Note that these posts are based on the [first edition] of this blog and might be out of date.
458
458
459
459
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
@@ -229,7 +229,7 @@ The _Privilege Stack Table_ is used by the CPU when the privilege level changes.
229
229
### Creating a TSS
230
230
Let's create a new TSS that contains a separate double fault stack in its interrupt stack table. For that we need a TSS struct. Fortunately, the `x86_64` crate already contains a [`TaskStateSegment` struct] that we can use.
We create the TSS in a new `gdt` module (the name will make sense later):
235
235
@@ -375,8 +375,8 @@ pub fn init() {
375
375
376
376
We reload the code segment register using [`set_cs`] and load the TSS using [`load_tss`]. The functions are marked as `unsafe`, so we need an `unsafe` block to invoke them. The reason is that it might be possible to break memory safety by loading invalid selectors.
Copy file name to clipboardexpand all lines: blog/content/edition-2/posts/07-hardware-interrupts/index.md
+16-16
Original file line number
Diff line number
Diff line change
@@ -75,29 +75,29 @@ Each controller can be configured through two [I/O ports], one “command” por
75
75
76
76
The default configuration of the PICs is not usable, because it sends interrupt vector numbers in the range 0–15 to the CPU. These numbers are already occupied by CPU exceptions, for example number 8 corresponds to a double fault. To fix this overlapping issue, we need to remap the PIC interrupts to different numbers. The actual range doesn't matter as long as it does not overlap with the exceptions, but typically the range 32–47 is chosen, because these are the first free numbers after the 32 exception slots.
77
77
78
-
The configuration happens by writing special values to the command and data ports of the PICs. Fortunately there is already a crate called [`pic8259_simple`], so we don't need to write the initialization sequence ourselves. In case you are interested how it works, check out [its source code][pic crate source], it's fairly small and well documented.
78
+
The configuration happens by writing special values to the command and data ports of the PICs. Fortunately there is already a crate called [`pic8259`], so we don't need to write the initialization sequence ourselves. In case you are interested how it works, check out [its source code][pic crate source], it's fairly small and well documented.
The main abstraction provided by the crate is the [`ChainedPics`] struct that represents the primary/secondary PIC layout we saw above. It is designed to be used in the following way:
We use the [`initialize`] function to perform the PIC initialization. Like the `ChainedPics::new` function, this function is also unsafe because it can cause undefined behavior if the PIC is misconfigured.
If all goes well we should continue to see the "It did not crash" message when executing `cargo run`.
131
131
@@ -200,15 +200,15 @@ lazy_static! {
200
200
}
201
201
202
202
extern"x86-interrupt"fntimer_interrupt_handler(
203
-
_stack_frame:&mutInterruptStackFrame)
203
+
_stack_frame:InterruptStackFrame)
204
204
{
205
205
print!(".");
206
206
}
207
207
```
208
208
209
209
Our `timer_interrupt_handler` has the same signature as our exception handlers, because the CPU reacts identically to exceptions and external interrupts (the only difference is that some exceptions push an error code). The [`InterruptDescriptorTable`] struct implements the [`IndexMut`] trait, so we can access individual entries through array indexing syntax.
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:
@@ -225,7 +225,7 @@ To send the EOI, we use our static `PICS` struct again:
The [`without_interrupts`] function takes a [closure] and executes it in an interrupt-free environment. We use it to ensure that no interrupt can occur as long as the `Mutex` is locked. When we run our kernel now we see that it keeps running without hanging. (We still don't notice any dots, but this is because they're scrolling by too fast. Try to slow down the printing, e.g. by putting a `for _ in 0..10000 {}` inside the loop.)
We use the [`Port`] type of the `x86_64` crate to read a byte from the keyboard's data port. This byte is called the [_scancode_] and is a number that represents the key press/release. We don't do anything with the scancode yet, we just print it to the screen:
0 commit comments