mirror of
https://github.com/cfenollosa/os-tutorial.git
synced 2024-10-27 20:34:19 +00:00
81 lines
3.2 KiB
Markdown
81 lines
3.2 KiB
Markdown
*Concepts you may want to Google beforehand: C types and structs, include guards, type attributes: packed, extern, volatile, exceptions*
|
|
|
|
**Goal: Set up the Interrupt Descriptor Table to handle CPU interrupts**
|
|
|
|
This lesson and the following ones have been heavily inspired
|
|
by [JamesM's tutorial](https://web.archive.org/web/20160412174753/http://www.jamesmolloy.co.uk/tutorial_html/index.html)
|
|
|
|
Data types
|
|
----------
|
|
|
|
First, we will define some special data types in `cpu/types.h`,
|
|
which will help us uncouple data structures for raw bytes from chars and ints.
|
|
It has been carefully placed on the `cpu/` folder, where we will
|
|
put machine-dependent code from now on. Yes, the boot code
|
|
is specifically x86 and is still on `boot/`, but let's leave
|
|
that alone for now.
|
|
|
|
Some of the already existing files have been changed to use
|
|
the new `u8`, `u16` and `u32` data types.
|
|
|
|
From now on, our C header files will also have include guards.
|
|
|
|
|
|
Interrupts
|
|
----------
|
|
|
|
Interrupts are one of the main things that a kernel needs to
|
|
handle. We will implement it now, as soon as possible, to be able
|
|
to receive keyboard input in future lessons.
|
|
|
|
Another examples of interrupts are: divisions by zero, out of bounds,
|
|
invalid opcodes, page faults, etc.
|
|
|
|
Interrupts are handled on a vector, with entries which are
|
|
similar to those of the GDT (lesson 9). However, instead of
|
|
programming the IDT in assembly, we'll do it in C.
|
|
|
|
`cpu/idt.h` defines how an idt entry is stored `idt_gate` (there need to be
|
|
256 of them, even if null, or the CPU may panic) and the actual
|
|
idt structure that the BIOS will load, `idt_register` which is
|
|
just a memory address and a size, similar to the GDT register.
|
|
|
|
Finally, we define a couple variables to access those data structures
|
|
from assembler code.
|
|
|
|
`cpu/idt.c` just fills in every struct with a handler.
|
|
As you can see, it is a matter
|
|
of setting the struct values and calling the `lidt` assembler command.
|
|
|
|
|
|
ISRs
|
|
----
|
|
|
|
The Interrupt Service Routines run every time the CPU detects an
|
|
interrupt, which is usually fatal.
|
|
|
|
We will write just enough code to handle them, print an error message,
|
|
and halt the CPU.
|
|
|
|
On `cpu/isr.h` we define 32 of them, manually. They are declared as
|
|
`extern` because they will be implemented in assembler, in `cpu/interrupt.asm`
|
|
|
|
Before jumping to the assembler code, check out `cpu/isr.c`. As you can see,
|
|
we define a function to install all isrs at once and load the IDT, a list of error
|
|
messages, and the high level handler, which kprints some information. You
|
|
can customize `isr_handler` to print/do whatever you want.
|
|
|
|
Now to the low level which glues every `idt_gate` with its low-level and
|
|
high-level handler. Open `cpu/interrupt.asm`. Here we define a common
|
|
low level ISR code, which basically saves/restores the state and calls
|
|
the C code, and then the actual ISR assembler functions which are referenced
|
|
on `cpu/isr.h`
|
|
|
|
Note how the `registers_t` struct is a representation of all the registers
|
|
we pushed in `interrupt.asm`
|
|
|
|
That's basically it. Now we need to reference `cpu/interrupt.asm` from our
|
|
Makefile, and make the kernel install the ISRs and launch one of them.
|
|
Notice how the CPU doesn't halt even though it would be good practice
|
|
to do it after some interrupts.
|