Здесь вы увидите несколько статей, которые описывают полный цикл загрузки ядра:
* [От загрузчика к ядру](linux-bootstrap-1.md) - описывает все стадии от включения компьютера до запуска первой инструкции ядра.
@ -8,3 +8,4 @@
* [Инициализация видеорежима и переход в защищённый режим](linux-bootstrap-3.md) - описывает инициализацию видеорежима в коде настройки ядра и переход в защищённый режим.
* [Переход в 64-битный режим](linux-bootstrap-4.md) - описывает подготовку к переходу в 64-битный режим и детали перехода.
* [Декомпрессия ядра](linux-bootstrap-5.md) - описывает подготовку перед декомпрессией ядра и детали самой декомпрессии.
* [Рандомизация адреса ядра](linux-bootstrap-6.md) - описывает рандомизацию адреса загрузки ядра Linux.
Основная причина не использовать [режим реальных адресов](http://wiki.osdev.org/Real_Mode) заключается в том, что возможен лишь очень ограниченный доступ к оперативной памяти. Как вы помните из предыдущей части, есть только 2<sup>20</sup> байт или 1 мегабайт, а иногда даже 640 килобайт оперативной памяти, доступной в режиме реальных адресов.
Защищённый режим принёс много изменений, но главным является отличие в управлении памятью. 20-битная адресная шина была заменена на 32-битную. Это позволило обеспечить доступ к 4 Гб памяти против 1 мегабайта в режиме реальных адресов. Также была добавлена поддержка [подкачки страниц](http://en.wikipedia.org/wiki/Paging), про которую вы можете прочитать в следующих разделах.
Защищённый режим принёс много изменений, но главным является отличие в управлении памятью. 20-битная адресная шина была заменена на 32-битную. Это позволило обеспечить доступ к 4 Гб памяти против 1 мегабайта в режиме реальных адресов. Также была добавлена поддержка [страничной организации памяти](http://en.wikipedia.org/wiki/Paging), про которую вы можете прочитать в следующих разделах.
Управление памятью в защищённом режиме разделяется на две, почти независимые части:
* Сегментация
* Подкачка страниц
* Страничная организация
Здесь мы будем рассматривать только сегментацию. Подкачка страниц будет обсуждаться в следующих разделах.
Здесь мы будем рассматривать только сегментацию. Страничная организация будет обсуждаться в следующих разделах.
Как вы можете помнить из предыдущей части, адреса в режиме реальных адресов состоят из двух частей:
@ -162,7 +162,7 @@ lgdt gdt
* Селектор сегмента должен быть загружен в один из сегментных регистров
* CPU пытается найти дескриптор сегмента по адресу GDT + Index из селектора и загрузить дескриптор в *скрытую* часть сегментного регистра
* Базовый адрес (из дескриптора сегмента) + смещение будет линейным адресом сегмента, который является физическим адресом (если подкачка страниц отключена).
* Базовый адрес (из дескриптора сегмента) + смещение будет линейным адресом сегмента, который является физическим адресом (если страничная организация отключена).
Это четвёртая часть `Процесса загрузки ядра`, в которой вы увидите первые шаги в [защищённом режиме](http://en.wikipedia.org/wiki/Protected_mode), такие как проверка поддержки процессором [long mode](http://en.wikipedia.org/wiki/Long_mode) и [SSE](http://en.wikipedia.org/wiki/Streaming_SIMD_Extensions), [подкачка страниц](http://en.wikipedia.org/wiki/Paging), инициализация таблиц страниц и в конце мы обсудим переход в [long mode](https://en.wikipedia.org/wiki/Long_mode).
Это четвёртая часть `Процесса загрузки ядра`, в которой вы увидите первые шаги в [защищённом режиме](http://en.wikipedia.org/wiki/Protected_mode), такие как проверка поддержки процессором [long mode](http://en.wikipedia.org/wiki/Long_mode) и [SSE](http://en.wikipedia.org/wiki/Streaming_SIMD_Extensions), [страничная организация памяти](http://en.wikipedia.org/wiki/Paging), инициализация таблиц страниц и в конце мы обсудим переход в [long mode](https://en.wikipedia.org/wiki/Long_mode).
**ЗАМЕЧАНИЕ: данная часть содержит много ассемблерного кода, так что если вы не знакомы с ним, вы можете прочитать соответствующую литературу**
@ -422,9 +422,9 @@ Long mode является расширением унаследованного
* Создать таблицу страниц и загрузить адрес таблицы страниц верхнего уровня в регистр `cr3`;
* Включить `EFER.LME`;
* Включить подкачку страниц.
* Включить страничную организацию памяти.
Мы уже включили `PAE` путём установки бита `PAE` в регистре управления `cr4`. Наша следующая цель - создать структуру для [подкачки страниц](https://en.wikipedia.org/wiki/Paging). Мы увидим это в следующем параграфе.
Мы уже включили `PAE` путём установки бита `PAE` в регистре управления `cr4`. Наша следующая цель - создать структуру для [страничной организации](https://en.wikipedia.org/wiki/Paging). Мы увидим это в следующем параграфе.
@ -433,7 +433,7 @@ Long mode является расширением унаследованного
**ПРИМЕЧАНИЕ: я не буду описывать теорию виртуальной памяти. Если вам необходимо больше информации по виртуальной памяти, см. ссылки в конце этой части.**
Ядро Linux использует `4 уровневую`подкачку страниц, и в целом мы создадим 6 таблиц страниц:
Ядро Linux использует `4 уровневую`страничную организацию, и в целом мы создадим 6 таблиц страниц:
* Одну таблицу `PML4 (карта страниц 4 уровня, Page Map Level 4)`с одной записью;
* Одну таблицу `PDP (указатель директорий страниц, Page Directory Pointer)`с четырьмя записями;
@ -497,7 +497,7 @@ pgtable:
jnz 1b
```
Мы помещаем базовый адрес указателя директорий страниц, который равен `4096` или, другими словами, смещение `0x1000` от таблицы `pgtable` в `edi`, и адрес первой записи указателя директорий страниц в регистр `eax`. Значение `4`, помещённое в регистр `ecx`, будет счётчиком в следующем цикле, в котором мы записываем адрес первой записи таблицы указателя директорий страниц в регистр `edi`. После этого `edi` будет содержать адрес первой записи указателя директорий страниц с флагами `0x7`. Далее мы просто вычисляем адрес следующих записей указателя директорий страниц, где каждая запись имеет размер `8` байт, и записываем их адреса в `eax`. Последний шаг в создании структуры подкачки страниц - создание `2048` записей с`2 мегабайтными` страницами:
Мы помещаем базовый адрес указателя директорий страниц, который равен `4096` или, другими словами, смещение `0x1000` от таблицы `pgtable` в `edi`, и адрес первой записи указателя директорий страниц в регистр `eax`. Значение `4`, помещённое в регистр `ecx`, будет счётчиком в следующем цикле, в котором мы записываем адрес первой записи таблицы указателя директорий страниц в регистр `edi`. После этого `edi` будет содержать адрес первой записи указателя директорий страниц с флагами `0x7`. Далее мы просто вычисляем адрес следующих записей указателя директорий страниц, где каждая запись имеет размер `8` байт, и записываем их адреса в `eax`. Последний шаг в создании страничной организации памяти - создание `2048` записей с`2 мегабайтными` страницами:
```assembly
leal pgtable + 0x2000(%ebx), %edi
@ -547,7 +547,7 @@ pgtable:
leal startup_64(%ebp), %eax
```
После этого мы помещаем адрес в стек и включаем поддержку подкачки страниц путём установки битов `PG` и `PE` в регистре `cr0`:
После этого мы помещаем адрес в стек и включаем поддержку страничной организации путём установки битов `PG` и `PE` в регистре `cr0`:
```assembly
pushl %eax
@ -590,10 +590,10 @@ ENTRY(startup_64)
* [Документация для разработчиков ПО на архитектуре Intel® 64 и IA-32](http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html)
Мы сравниваем нижнюю часть регистра `rbp`с дополняемым значением `PMD_PAGE_MASK`. `PMD_PAGE_MASK` указывает маску для `каталога страниц среднего уровня` (см. [подкачку страниц](../Theory/Paging.md)) и определён как:
Мы сравниваем нижнюю часть регистра `rbp`с дополняемым значением `PMD_PAGE_MASK`. `PMD_PAGE_MASK` указывает маску для `промежуточного каталога страниц` (см. [страничную организацию памяти](../Theory/Paging.md)) и определён как:
Вы можете больше узнать об этом в статье [Подкачка страниц](../Theory/Paging.html).
Вы можете больше узнать об этом в статье [страничная организация памяти](../Theory/Paging.html).
`level3_kernel_pgt` хранит две записи, которые отображают пространство ядра. В начале его определения мы видим, что он заполнен нулями `L3_START_KERNEL` или `510` раз. `L3_START_KERNEL` - это индекс в верхнем каталоге страниц, который содержит адрес `__START_KERNEL_map` и равен `510`. После этого мы можем видеть определение двух записей `level3_kernel_pgt`: `level2_kernel_pgt` и `level2_fixmap_pgt`. Первая очень проста - это запись в таблице страниц, которая содержит указатель на каталог страниц среднего уровня, который отображает пространство ядра и содержит права доступа:
`level3_kernel_pgt` хранит две записи, которые отображают пространство ядра. В начале его определения мы видим, что он заполнен нулями `L3_START_KERNEL` или `510` раз. `L3_START_KERNEL` - это индекс в верхнем каталоге страниц, который содержит адрес `__START_KERNEL_map` и равен `510`. После этого мы можем видеть определение двух записей `level3_kernel_pgt`: `level2_kernel_pgt` и `level2_fixmap_pgt`. Первая очень проста - это запись в таблице страниц, которая содержит указатель на промежуточный каталог страниц, который отображает пространство ядра и содержит права доступа:
Теперь мы можем увидеть настройку отображения "один в один" начальных таблиц страниц. Вподкачке, отображённой "один в один", виртуальные адреса сопоставляются с физическими адресами, которые имеют одно и то же значение, `один в один`. Давайте рассмотрим это подробнее. Прежде всего, мы получаем `rip-относительные` адреса `_text` и `_early_level4_pgt` и помещаем их в регистры `rdi` и `rbx`:
Теперь мы можем увидеть настройку отображения "один в один" начальных таблиц страниц. Встраничной организации с отображением "один в один", виртуальные адреса сопоставляются с физическими адресами, которые имеют одно и то же значение, `один в один`. Давайте рассмотрим это подробнее. Прежде всего, мы получаем `rip-относительные` адреса `_text` и `_early_level4_pgt` и помещаем их в регистры `rdi` и `rbx`:
На данный момент это всё. Наша ранняя подкачка страниц настроена и нам нужно совершить последнее приготовление, прежде чем мы перейдём к коду на C и к точке входа в ядро.
На данный момент это всё. Наша ранняя страничная структура настроена и нам нужно совершить последнее приготовление, прежде чем мы перейдём к коду на C и к точке входа в ядро.
Последнее приготовление перед переходом на точку входа в ядро
В предыдущей [части](linux-initialization-1.md) мы остановились перед настройкой начальных обработчиков прерываний. На данный момент мы находимся в распакованном ядре Linux, у нас есть базовая структура [подкачки](https://en.wikipedia.org/wiki/Page_table) для начальной загрузки, и наша текущая цель - завершить начальную подготовку до того, как основной код ядра начнёт свою работу.
В предыдущей [части](linux-initialization-1.md) мы остановились перед настройкой начальных обработчиков прерываний. На данный момент мы находимся в распакованном ядре Linux, у нас есть базовая структура [страничной организации памяти](https://en.wikipedia.org/wiki/Page_table) для начальной загрузки, и наша текущая цель - завершить начальную подготовку до того, как основной код ядра начнёт свою работу.
Мы уже начали эту подготовку в предыдущей [первой](linux-initialization-1.md) части этой [главы](README.md). Мы продолжим в этой части и узнаем больше об обработке прерываний и исключений.
@ -475,7 +475,7 @@ pud_p += pud_index(address);
pud = *pud_p;
```
На следующем шаге мы делаем те же действия что и ранее, но со средним каталогом страниц. В конце мы исправляем адрес среднего каталога страниц, который содержит отображения текста ядра+виртуальные адреса данных:
На следующем шаге мы делаем те же действия что и ранее, но с промежуточным каталогом страниц. В конце мы исправляем адрес промежуточного каталога страниц, который содержит отображения текста ядра+виртуальные адреса данных: