In the previous [part](https://proninyaroslav.gitbooks.io/linux-insides-ru/content/Initialization/linux-initialization-1.html) we stopped before setting of early interrupt handlers. At this moment we are in the decompressed Linux kernel, we have basic [paging](https://en.wikipedia.org/wiki/Page_table) structure for early boot and our current goal is to finish early preparation before the main kernel code will start to work.
В предыдущей [части](linux-initialization-1.md) мы остановились перед настройкой начальных обработчиков прерываний. На данный момент мы находимся в распакованном ядре Linux, у нас есть базовая структура [подкачки](https://en.wikipedia.org/wiki/Page_table) для начальной загрузки, и наша текущая цель - завершить начальную подготовку до того, как основной код ядра начнёт свою работу.
We already started to do this preparation in the previous [first](https://proninyaroslav.gitbooks.io/linux-insides-ru/content/Initialization/linux-initialization-1.html) part of this [chapter](https://proninyaroslav.gitbooks.io/linux-insides-ru/content/Initialization/index.html). We continue in this part and will know more about interrupt and exception handling.
Мы уже начали эту подготовку в предыдущей [первой](linux-initialization-1.md) части этой [главы](README.md). Мы продолжим в этой части и узнаем больше об обработке прерываний и исключений.
Remember that we stopped before following loop:
Как вы можете помнить, мы остановились перед этим циклом:
```C
for (i = 0; i <NUM_EXCEPTION_VECTORS;i++)
set_intr_gate(i, early_idt_handler_array[i]);
```
from the [arch/x86/kernel/head64.c](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/x86/kernel/head64.c) source code file. But before we started to sort out this code, we need to know about interrupts and handlers.
из файла [arch/x86/kernel/head64.c](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/x86/kernel/head64.c). Но прежде чем начать разбирать этот код, нам нужно знать о прерываниях и обработчиках.
An interrupt is an event caused by software or hardware to the CPU. For example a user have pressed a key on keyboard. On interrupt, CPU stops the current task and transfer control to the special routine which is called - [interrupt handler](https://en.wikipedia.org/wiki/Interrupt_handler). An interrupt handler handles and interrupt and transfer control back to the previously stopped task. We can split interrupts on three types:
Прерывание - это событие, вызванное программным или аппаратным обеспечением в CPU. Например, пользователь нажал клавишу на клавиатуре. Во время прерывания, CPU останавливает текущую задачу и передаёт управление специальной процедуре - [обработчику прерываний](https://en.wikipedia.org/wiki/Interrupt_handler). Обработчик прерываний обрабатывает прерывания и передаёт управление обратно к ранее остановленной задаче. Мы можем разделить прерывания на три типа:
* Software interrupts - when a software signals CPU that it needs kernel attention. These interrupts are generally used for system calls;
* Hardware interrupts - when a hardware event happens, for example button is 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.
* Программные прерывания - когда программное обеспечение сигнализирует CPU, что ему нужно обратиться к ядру. Эти прерывания обычно используются для системных вызовов;
* Аппаратные прерывания - когда происходит аппаратное событие, например нажатие кнопки на клавиатуре;
* Исключения - прерывания, генерируемые процессором, когда CPU обнаруживает ошибку, например деление на ноль или доступ к странице памяти, которая не находится в ОЗУ.
Every interrupt and exception is assigned a 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 `32` to `255` are used for user-defined interrupts. We can see it in the code above - `NUM_EXCEPTION_VECTORS`, which defined as:
Каждому прерыванию и исключению присваивается уникальный номер - `номер вектора`. `Номер вектора` может быть любым числом от `0` до `255`. Существует обычная практика использовать первые `32` векторных номеров для исключений, а номера от `32` до `255` для пользовательских прерываний. Мы можем видеть это в коде выше - `NUM_EXCEPTION_VECTORS`, определённый как:
```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 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:
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` instruction for loading base address of the table into this register.
64-bit mode IDT entry has following structure:
CPU использует номер вектора как индекс в `таблице векторов прерываний` (мы рассмотрим её позже). Для перехвата прерываний CPU использует [APIC](http://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller). В следующей таблице показаны исключения `0-31`:
* Ошибки (Faults) - исключения, по окончании обработки которых прерванная команда повторяется;
* Ловушки (Traps) - исключения, при обработке которых CPU сохраняет состояние, следующее за командой, вызвавшей исключение;
* Аварии (Aborts) - исключения, при обработке которых CPU не сохраняет состояния и не имеет возможности вернуться к месту
исключения
Для реагирования на прерывание CPU использует специальную структуру - таблицу векторов прерываний (Interrupt Descriptor Table, IDT). IDT является массивом 8-байтных дескрипторов, наподобие глобальной таблицы дескрипторов, но записи в IDT называются `шлюзами` (gates). CPU умножает номер вектора на 8 для того чтобы найти индекс записи IDT. Но в 64-битном режиме IDT представляет собой массив 16-байтных дескрипторов и CPU умножает номер вектора на 16. Из предыдущей части мы помним, что CPU использует специальный регистр `GDTR` для поиска глобальной таблицы дескрипторов, поэтому CPU использует специальный регистр `IDTR` для таблицы векторов прерываний и инструкцию `lidt` для загрузки базового адреса таблицы в этот регистр.
Запись IDT в 64-битном режиме имеет следующую структуру:
* `Селектор сегмента` - селектор сегмента кода в GDT или LDT
* `IST` - обеспечивает возможность переключения на новый стек для обработки прерываний.
And the last `Type` field describes type of the `IDT` entry. There are three different kinds of handlers for interrupts:
И последнее поле `Тип` описывает тип записи `IDT`. Существует три различных типа обработчиков для прерываний:
* Task descriptor
* 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 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.
Дескрипторы прерываний и ловушек содержат дальний указатель на точку входа обработчика прерываний. Различие между этими типами заключается в том, как CPU обрабатывает флаг `IF`. Если обработчик прерываний был вызван через шлюз прерывания, CPU очищает флаг `IF` чтобы предотвратить другие прерывания, пока выполняется текущий обработчик прерываний. После выполнения текущего обработчика прерываний CPU снова устанавливает флаг `IF`с помощью инструкции `iret`.
Other bits in the interrupt gate reserved and must be 0. Now let's look how CPU handles interrupts:
Остальные биты в шлюзе прерывания зарезервированы и должны быть равны 0. Теперь давайте посмотрим, как CPU обрабатывает прерывания:
* 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.
* CPU сохраняет регистр флагов, `CS`, и указатель на инструкцию в стеке.
* Если прерывание вызывает код ошибки (например, `#PF`), CPU сохраняет ошибку в стеке после указателя на инструкцию;
* После выполнения обработчика прерываний для возврата из него используется инструкция `iret`.
@ -220,7 +227,7 @@ As I mentioned above, we fill gate descriptor in this function. We fill three pa
#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 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.
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`:
@ -453,7 +460,7 @@ if (next_early_pgt >= EARLY_DYNAMIC_PAGE_TABLES) {