diff --git a/Booting/linux-bootstrap-1.md b/Booting/linux-bootstrap-1.md index 5e9ecf3..7aa3250 100644 --- a/Booting/linux-bootstrap-1.md +++ b/Booting/linux-bootstrap-1.md @@ -138,7 +138,7 @@ nasm -f bin boot.nasm && qemu-system-x86_64 boot Вы увидите: -![Простой загрузчик, который печатает только `!`](http://oi60.tinypic.com/2qbwup0.jpg) +![Простой загрузчик, который печатает только `!`](images/simple_bootloader.png) В этом примере мы можем видеть, что код будет выполнен в 16-битном режиме реальных адресов и начнёт выполнение с адреса `0x7c00`. После запуска он вызывает прерывание [0x10](http://www.ctyme.com/intr/rb-0106.htm), которое просто печатает символ `!`. Оставшиеся 510 байт заполняются нулями, и код заканчивается двумя магическими байтами `0xaa` и `0x55`. @@ -247,7 +247,7 @@ X + sizeof(KernelBootSector) + 1 ``` где `X` - это адрес загруженного сектора загрузки ядра. В моем случае `X` это `0x10000`, как мы можем видеть в дампе памяти: -![Первый адрес ядра](http://oi57.tinypic.com/16bkco2.jpg) +![Первый адрес ядра](images/kernel_first_address.png) Сейчас загрузчик поместил ядро Linux в память, заполнил поля заголовка, а затем переключился на него. Теперь мы можем перейти непосредственно к коду настройки ядра. @@ -263,7 +263,7 @@ qemu-system-x86_64 vmlinuz-3.18-generic то увидите: -![Попытка использовать vmlinuz в qemu](http://oi60.tinypic.com/r02xkz.jpg) +![Попытка использовать vmlinuz в qemu](images/try_vmlinuz_in_qemu.png) На самом деле, `header.S` начинается с [MZ](https://en.wikipedia.org/wiki/DOS_MZ_executable) (см. картинку выше), вывода сообщения об ошибке и [PE](https://en.wikipedia.org/wiki/Portable_Executable) заголовка: @@ -403,7 +403,7 @@ _start: Здесь мы видим выравнивание сегмента `dx` (содержащего значение `sp`, полученное загрузчиком) до 4 байт и проверку - является ли полученное значение нулём. Если ноль, то помещаем `0xfffx` (выровненный до `4` байт адрес до максимального значения сегмента в 64 Кб) в `dx`. Если не ноль, продолжаем использовать `sp`, полученный от загрузчика (в моём случае `0xf7f4`). После этого мы помещаем значение `ax` в `ss`, который хранит корректный адрес сегмента `0x1000` и устанавливает корректное значение `sp`. Теперь мы имеем корректный стек: -![стек](http://oi58.tinypic.com/16iwcis.jpg) +![стек](images/stack1.png) * Второй сценарий (когда `ss` != `ds`). Во-первых, помещаем значение [_end](https://github.com/torvalds/linux/blob/v4.16/arch/x86/boot/setup.ld#L52) (адрес окончания кода настройки) в `dx` и проверяем поле заголовка `loadflags` инструкцией `testb`, чтобы понять, можем ли мы использовать кучу (heap). [loadflags](https://github.com/torvalds/linux/blob/v4.16/arch/x86/boot/header.S#L321) является заголовком с битовой маской, который определён как: @@ -427,11 +427,11 @@ _start: ``` Если бит `CAN_USE_HEAP` установлен, мы помещаем `heap_end_ptr` в `dx` (который указывает на `_end`) и добавляем к нему `STACK_SIZE` (минимальный размер стека, `512` байт). После этого, если `dx` без переноса (будет без переноса, поскольку `dx = _end + 512`), переходим на метку `2` (как в предыдущем случае) и создаём корректный стек. -![стек](http://oi62.tinypic.com/dr7b5w.jpg) +![стек](images/stack2.png) * Если флаг `CAN_USE_HEAP` не установлен, мы просто используем минимальный стек от `_end` до `_end + STACK_SIZE`: -![минимальный стек](http://oi60.tinypic.com/28w051y.jpg) +![минимальный стек](images/minimal_stack.png) Настройка BSS -------------------------------------------------------------------------------- @@ -458,7 +458,7 @@ _start: ``` Во-первых, адрес [__bss_start](https://github.com/torvalds/linux/blob/v4.16/arch/x86/boot/setup.ld#L47) помещается в `di`. Далее, адрес `_end + 3` (+3 - выравнивает до 4 байт) помещается в `cx`. Регистр `eax` очищается (с помощью инструкции `xor`), а размер секции BSS (`cx`-`di`) вычисляется и помещается в `cx`. Затем `cx` делится на 4 (размер 'слова' (англ. word)), а инструкция `stosl` используется повторно, сохраняя значение `eax` (ноль) в адрес, на который указывает `di`, автоматически увеличивая `di` на 4 (это продолжается до тех пор, пока `cx` не достигнет нуля). Эффект от этого кода в том, что теперь все 'слова' в памяти от `__bss_start` до `_end` заполнены нулями: -![bss](http://oi59.tinypic.com/29m2eyr.jpg) +![bss](images/bss.png) Переход к основному коду -------------------------------------------------------------------------------- diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index ec27e1d..a00e8ca 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -166,7 +166,7 @@ lgdt gdt Схематично это будет выглядеть следующим образом: -![линейный адрес](http://oi62.tinypic.com/2yo369v.jpg) +![линейный адрес](images/linear_address.png) Алгоритм перехода из режима реальных адресов в защищённый режим: diff --git a/Booting/linux-bootstrap-3.md b/Booting/linux-bootstrap-3.md index 80306bd..80b1f83 100644 --- a/Booting/linux-bootstrap-3.md +++ b/Booting/linux-bootstrap-3.md @@ -40,7 +40,7 @@ vga= Таким образом, мы можем добавить параметр `vga` в конфигурационный файл GRUB (или любого другого загрузчика) и он передаст его в командную строку ядра. Как говорится в описании, этот параметр может иметь разные значения. Например, это может быть целым числом `0xFFFD` или `ask`. Если передать `ask` в `vga`, вы увидите примерно такое меню: -![video mode setup menu](http://oi59.tinypic.com/ejcz81.jpg) +![video mode setup menu](images/video_mode_setup_menu.png) которое попросит выбрать видеорежим. Мы посмотрим на его реализацию, но перед этим рассмотрим некоторые другие вещи.