Merge pull request #68 from akash0x53/linux-initialization-2-fix

Some spelling & sentence corrections.
pull/69/head
0xAX 9 years ago
commit 27c09a0042

@ -1,12 +1,12 @@
Kernel initialization. Part 2.
================================================================================
Early interupt and exception handling
Early interrupt and exception handling
--------------------------------------------------------------------------------
In the previous [part](http://0xax.gitbooks.io/linux-insides/content/Initialization/linux-initialization-1.html) we stoped before setting of early interrupt handlers. We continue in this part and will know more about interrupt and exception handling.
In the previous [part](http://0xax.gitbooks.io/linux-insides/content/Initialization/linux-initialization-1.html) we stopped before setting of early interrupt handlers. We continue in this part and will know more about interrupt and exception handling.
Remeber that we stoped before following loop:
Remember that we stopped before following loop:
```C
for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
@ -18,19 +18,19 @@ from the [arch/x86/kernel/head64.c](https://github.com/torvalds/linux/blob/maste
Some theory
--------------------------------------------------------------------------------
Interrupt is an event caused by software or hardware to the CPU. CPU stops current task after an interrupt and transfer control to the interruption handler, which handles interruption and transfers control back to the interrupted task. We can split interrupts on three types:
Interrupt is an event caused by software or hardware to the CPU. On interrupt, CPU stops the current task and transfer control to the interrupt handler, which handles interruption and transfer control back to the previously stopped task. We can split interrupts on three types:
* Software interrupts - when a software signals CPU that it needs kernel attention. These interrupts generally used for system calls;
* Hardware interrupts - when a hardware, for example button pressed on a keyboard;
* Exceptions - interrupts generated by CPU, when the CPU detects error, for example division by zero or accessing a memory page which is not in RAM.
Every interrupt and exception is assigned an unique number which called - `vector number`. `vector number` can be any number from `0` to `255`. There is common practice to use first `32` vector numbers for exceptions, and vector numbers from `31` to `255` are used for user-defined interrupts. We can see it in the code above - `NUM_EXCEPTION_VECTORS`, which defined as:
Every interrupt and exception is assigned an unique number which called - `vector number`. `Vector number` can be any number from `0` to `255`. There is common practice to use first `32` vector numbers for exceptions, and vector numbers from `31` to `255` are used for user-defined interrupts. We can see it in the code above - `NUM_EXCEPTION_VECTORS`, which defined as:
```C
#define NUM_EXCEPTION_VECTORS 32
```
CPU uses vector number as an index in the `Interrupt Descriptor Table` (we will see description of it soon). CPU cathes interrupts from the [APIC](http://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller) or through it's pins. Following table shows `0-31` exceptions:
CPU uses vector number as an index in the `Interrupt Descriptor Table` (we will see description of it soon). CPU catch interrupts from the [APIC](http://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller) or through it's pins. Following table shows `0-31` exceptions:
```
----------------------------------------------------------------------------------------------
@ -82,7 +82,7 @@ CPU uses vector number as an index in the `Interrupt Descriptor Table` (we will
----------------------------------------------------------------------------------------------
```
To react on interrupt CPU uses special structure - Interrupt Descriptor Table or IDT. IDT is an array of 8-byte descriptors like Global Descriptor Table, but IDT entries are called `gates`. CPU multiplies vector number on 8 to find index of the IDT entry. But 64-bit mode IDT is an array of 16-byte descriptors and CPU multiplies vector number on 16 to find index of the entry in the IDT. We remember from the previous part that CPU uses special `GDTR` register to locate Global Descriptor Table, so CPU uses special register `IDTR` for Interrupt Descriptor Table and `lidt` instruuction for loading base address of the table into this register.
To react on interrupt CPU uses special structure - Interrupt Descriptor Table or IDT. IDT is an array of 8-byte descriptors like Global Descriptor Table, but IDT entries are called `gates`. CPU multiplies vector number on 8 to find index of the IDT entry. But in 64-bit mode IDT is an array of 16-byte descriptors and CPU multiplies vector number on 16 to find index of the entry in the IDT. We remember from the previous part that CPU uses special `GDTR` register to locate Global Descriptor Table, so CPU uses special register `IDTR` for Interrupt Descriptor Table and `lidt` instruuction for loading base address of the table into this register.
64-bit mode IDT entry has following structure:
@ -127,13 +127,13 @@ And the last `Type` field describes type of the `IDT` entry. There are three dif
* Interrupt descriptor
* Trap descriptor
Interrupt and trap descriptors contain a far pointer to the entry point of the interrupt handler. Only one difference between these types is how CPU handles `IF` flag. If interrupt handler was accessed through interrupt gate, CPU clear `IF` flag to prevent other interrupts while current interrupt handler executes. After that current interrupt handler executes, CPU sets `IF` flag again with `iret` instruction.
Interrupt and trap descriptors contain a far pointer to the entry point of the interrupt handler. Only one difference between these types is how CPU handles `IF` flag. If interrupt handler was accessed through interrupt gate, CPU clear the `IF` flag to prevent other interrupts while current interrupt handler executes. After that current interrupt handler executes, CPU sets the `IF` flag again with `iret` instruction.
Other bits reserved and must be 0.
Now let's look how CPU handles interrupts:
* CPU saves flags register, `CS`, and instruction pointer on the stack.
* CPU save flags register, `CS`, and instruction pointer on the stack.
* If interrupt causes an error code (like `#PF` for example), CPU saves an error on the stack after instruction pointer;
* After interrupt handler executed, `iret` instruction used to return from it.
@ -142,7 +142,7 @@ Now let's back to code.
Fill and load IDT
--------------------------------------------------------------------------------
We stoped at the following point:
We stopped at the following point:
```C
for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
@ -151,7 +151,7 @@ for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
Here we call `set_intr_gate` in the loop, which takes two parameters:
* Number of interrupt;
* Number of an interrupt;
* Address of the idt handler.
and inserts an interrupt gate in the nth `IDT` entry. First of all let's look on the `early_idt_handlers`. It is an array which contains address of the first 32 interrupt handlers:
@ -208,7 +208,7 @@ static inline void pack_gate(gate_desc *gate, unsigned type, unsigned long func,
}
```
As i wrote above we fill gate descriptor in this function. We fill three parts of the address of the interrupt handler with the address which we got in the main loop (address of the interrupt handler entry point). We are useing three following macro to split address on three parts:
As mentioned above we fill gate descriptor in this function. We fill three parts of the address of the interrupt handler with the address which we got in the main loop (address of the interrupt handler entry point). We are using three following macro to split address on three parts:
```C
#define PTR_LOW(x) ((unsigned long long)(x) & 0xFFFF)
@ -216,7 +216,7 @@ As i wrote above we fill gate descriptor in this function. We fill three parts o
#define PTR_HIGH(x) ((unsigned long long)(x) >> 32)
```
With the first `PTR_LOW` macro we get the first 2 bytes of the address, with the second `PTR_MIDDLE` we get the second 2 bytes of the address and with the third `PTR_HIGH` macro we get the last 4 bytes of ther address. Next we setup the segment selector for interrupt handler, it will be our kernel code segment - `__KERNEL_CS`. In the next step we fill `Interrupt Stack Table` and `Descriptor Privilege Level` (highest privilege level) with zeros. And we set `GAT_INTERRUPT` type in the end.
With the first `PTR_LOW` macro we get the first 2 bytes of the address, with the second `PTR_MIDDLE` we get the second 2 bytes of the address and with the third `PTR_HIGH` macro we get the last 4 bytes of the address. Next we setup the segment selector for interrupt handler, it will be our kernel code segment - `__KERNEL_CS`. In the next step we fill `Interrupt Stack Table` and `Descriptor Privilege Level` (highest privilege level) with zeros. And we set `GAT_INTERRUPT` type in the end.
Now we have filled IDT entry and we can call `native_write_idt_entry` function which just copies filled `IDT` entry to the `IDT`:
@ -245,9 +245,9 @@ and `load_idt` just executes `lidt` instruction:
asm volatile("lidt %0"::"m" (*dtr));
```
You can note that there is calls of the `_trace_*` functions in the `_set_gate` and other functions. These functions fills `IDT` gates in the same maner that `_set_gate` but with one difference. These functions use `trace_idt_table` Interrupt Descriptor Table instead of `idt_table` for tracepoints (we will cover this theme in the another part).
You can note that there are calls of the `_trace_*` functions in the `_set_gate` and other functions. These functions fills `IDT` gates in the same manner that `_set_gate` but with one difference. These functions use `trace_idt_table` Interrupt Descriptor Table instead of `idt_table` for tracepoints (we will cover this theme in the another part).
Ok, now we have filled and loaded Interrupt Descriptor Table, we know how CPU acts during interrupt. So now time to deal with interrupts handlers.
Okay, now we have filled and loaded Interrupt Descriptor Table, we know how the CPU acts during interrupt. So now time to deal with interrupts handlers.
Early interrupts handlers
--------------------------------------------------------------------------------
@ -270,7 +270,7 @@ early_idt_handlers:
.endr
```
We can see here interrupt handlers generation for the first 32 execeptions. We make a check here, if exception has error code we do not nothing, if exception does not return error code we push zero to the stack. We do it for that would stack was uniform. After that we push exception number on the stack and jump on the `early_idt_handler` which is generic interrupt handler for now. As i wrote above, CPU pushes flag register, `CS` and `RIP` on the stack. So before `early_idt_handler` will be executed, stack will contain following data:
We can see here, interrupt handlers generation for the first 32 exceptions. We check here, if exception has error code then we do nothing, if exception does not return error code, we push zero to the stack. We do it for that would stack was uniform. After that we push exception number on the stack and jump on the `early_idt_handler` which is generic interrupt handler for now. As i wrote above, CPU pushes flag register, `CS` and `RIP` on the stack. So before `early_idt_handler` will be executed, stack will contain following data:
```
|--------------------|
@ -375,13 +375,13 @@ It starts from the definition of some variables which have `*val_t` types. All o
typedef unsigned long pgdval_t;
```
Also we will operate with the `*_t` (not val) types, for example `pgd_t` and etc... All of these types defined in the [arch/x86/include/asm/pgtable_types.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/pgtable_types.h) and represent strucutres like this:
Also we will operate with the `*_t` (not val) types, for example `pgd_t` and etc... All of these types defined in the [arch/x86/include/asm/pgtable_types.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/pgtable_types.h) and represent structures like this:
```C
typedef struct { pgdval_t pgd; } pgd_t;
```
For example
For example,
```C
extern pgd_t early_level4_pgt[PTRS_PER_PGD];

Loading…
Cancel
Save