From 598c3206353bcd6d2fe7b5a8f0c1ae1024abc658 Mon Sep 17 00:00:00 2001 From: proninyaroslav Date: Sun, 5 Aug 2018 11:31:19 +0300 Subject: [PATCH] Update linux-bootstrap-1.md --- Booting/linux-bootstrap-1.md | 46 +++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/Booting/linux-bootstrap-1.md b/Booting/linux-bootstrap-1.md index 22f8f74..1aade8c 100644 --- a/Booting/linux-bootstrap-1.md +++ b/Booting/linux-bootstrap-1.md @@ -37,7 +37,7 @@ CS base 0xffff0000 ``` Процессор начинает свою работу в [режиме реальных адресов](https://en.wikipedia.org/wiki/Real_mode). Давайте немного задержимся и попытаемся понять сегментацию памяти в этом режиме. Режим реальных адресов поддерживается всеми x86-совместимыми процессорами, от [8086](https://en.wikipedia.org/wiki/Intel_8086) до самых новых 64-битных CPU Intel. Процессор 8086 имел 20-битную шину адреса, т.е. он мог работать с адресным пространством в диапазоне 0-0xFFFFF (1 мегабайт). Но регистры у него были только 16-битные, а в таком случае максимальный размер адресуемой памяти составляет `2^16 - 1` или `0xffff` (64 килобайта). -[Сегментация памяти](http://en.wikipedia.org/wiki/Memory_segmentation) используется для того, чтобы задействовать всё доступное адресное пространство. Вся память делится на небольшие, фиксированного размера сегменты по `65536` байт (64 Кб) каждый. Поскольку мы не можем адресовать память свыше `64 Кб` с помощью 16-битных регистров, был придуман альтернативный метод. +[Сегментация памяти](https://en.wikipedia.org/wiki/Memory_segmentation) используется для того, чтобы задействовать всё доступное адресное пространство. Вся память делится на небольшие, фиксированного размера сегменты по `65536` байт (64 Кб) каждый. Поскольку мы не можем адресовать память свыше `64 Кб` с помощью 16-битных регистров, был придуман альтернативный метод. Адрес состоит из двух частей: селектора сегмента, который содержит базовый адрес, и смещение от этого базового адреса. В режиме реальных адресов базовый адрес селектора сегмента это `Селектор Сегмента * 16`. Таким образом, чтобы получить физический адрес в памяти, нужно умножить селектор сегмента на `16` и прибавить к нему смещение: @@ -73,29 +73,31 @@ CS base 0xffff0000 '0xfffffff0' ``` -Мы получили `0xfffffff0`, т.е. 16 байт ниже 4 Гб. По этому адресу располагается так называемый [вектор прерываний](http://en.wikipedia.org/wiki/Reset_vector). Это область памяти, в которой CPU ожидает найти первую инструкцию для выполнения после сброса. Она содержит инструкцию [jump](http://en.wikipedia.org/wiki/JMP_%28x86_instruction%29) (`jmp`), которая обычно указывает на точку входа в BIOS. Например, если мы взглянем на исходный код [coreboot](http://www.coreboot.org/) (`src/cpu/x86/16bit/reset16.inc`), то увидим следующее: +Мы получили `0xfffffff0`, т.е. 16 байт ниже 4 Гб. По этому адресу располагается так называемый [вектор прерываний](https://en.wikipedia.org/wiki/Reset_vector). Это область памяти, в которой CPU ожидает найти первую инструкцию для выполнения после сброса. Она содержит инструкцию [jump](https://en.wikipedia.org/wiki/JMP_%28x86_instruction%29) (`jmp`), которая обычно указывает на точку входа в BIOS. Например, если мы взглянем на исходный код [coreboot](https://www.coreboot.org/) (`src/cpu/x86/16bit/reset16.inc`), то увидим следующее: ```assembly - .section ".reset" + .section ".reset", "ax", %progbits .code16 -.globl reset_vector -reset_vector: +.globl _start +_start: .byte 0xe9 - .int _start - ( . + 2 ) + .int _start16bit - ( . + 2 ) ... ``` -Здесь мы можем видеть [опкод инструкции jmp](http://ref.x86asm.net/coder32.html#xE9) - `0xe9`, и его адрес назначения `_start - ( . + 2)`. +Здесь мы можем видеть [опкод инструкции jmp](http//ref.x86asm.net/coder32.html#xE9) - `0xe9`, и его адрес назначения `_start16bit - ( . + 2)`. -Мы также можем видеть, что секция `reset` занимает `16` байт и начинается с `0xfffffff0` (`src/cpu/x86/16bit/reset16.lds`): +Мы также можем видеть, что секция `reset` занимает `16` байт и начинается с `0xfffffff0` (`src/cpu/x86/16bit/reset16.ld`): ``` SECTIONS { + /* Trigger an error if I have an unuseable start address */ + _bogus = ASSERT(_start16bit >= 0xffff0000, "_start16bit too low. Please report."); _ROMTOP = 0xfffffff0; . = _ROMTOP; .reset . : { - *(.reset) - . = 15 ; + *(.reset); + . = 15; BYTE(0x00); } } @@ -132,7 +134,7 @@ db 0xaa nasm -f bin boot.nasm && qemu-system-x86_64 boot ``` -Команда оповещает эмулятор [QEMU](http://qemu.org) о необходимости использовать в качестве образа диска созданный только что бинарный файл. Пока последний проверяет, удовлетворяет ли загрузочный сектор всем необходимым требованиям (в origin записывается `0x7c00`, а в конце магическая последовательность), QEMU будет работать с бинарным файлом как с главной загрузочной записью (MBR) образа диска. +Команда оповещает эмулятор [QEMU](https://qemu.org) о необходимости использовать в качестве образа диска созданный только что бинарный файл. Пока последний проверяет, удовлетворяет ли загрузочный сектор всем необходимым требованиям (в origin записывается `0x7c00`, а в конце магическая последовательность), QEMU будет работать с бинарным файлом как с главной загрузочной записью (MBR) образа диска. Вы увидите: @@ -179,7 +181,7 @@ objdump -D -b binary -mi386 -Maddr16,data16,intel boot 0x000F0000 - 0x000FFFFF - Системная BIOS ``` -В начале статьи я написал, что первая инструкция, выполняемая CPU, расположена по адресу `0xFFFFFFF0`, значение которого намного больше, чем `0xFFFFF` (1 Мб). Каким образом CPU получает доступ к этому участку в режиме реальных адресов? Ответ на этот вопрос находится в документации [coreboot](http://www.coreboot.org/Developer_Manual/Memory_map): +В начале статьи я написал, что первая инструкция, выполняемая CPU, расположена по адресу `0xFFFFFFF0`, значение которого намного больше, чем `0xFFFFF` (1 Мб). Каким образом CPU получает доступ к этому участку в режиме реальных адресов? Ответ на этот вопрос находится в документации [coreboot](https://www.coreboot.org/Developer_Manual/Memory_map): ``` 0xFFFE_0000 - 0xFFFF_FFFF: 128 Кб ROM отображаются на адресное пространство @@ -189,9 +191,9 @@ objdump -D -b binary -mi386 -Maddr16,data16,intel boot Загрузчик -------------------------------------------------------------------------------- -Существует несколько загрузчиков Linux, такие как [GRUB 2](https://www.gnu.org/software/grub/) и [syslinux](http://www.syslinux.org/wiki/index.php/The_Syslinux_Project). Ядро Linux имеет [протокол загрузки](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/Documentation/x86/boot.txt), который определяет требования к загрузчику для реализации поддержки Linux. В этом примере будет описан GRUB 2. +Существует несколько загрузчиков Linux, такие как [GRUB 2](https://www.gnu.org/software/grub/) и [syslinux](https://www.syslinux.org/wiki/index.php/The_Syslinux_Project). Ядро Linux имеет [протокол загрузки](https://github.com/torvalds/linux/blob/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/Documentation/x86/boot.txt), который определяет требования к загрузчику для реализации поддержки Linux. В этом примере будет описан GRUB 2. -Теперь, когда BIOS выбрал загрузочное устройство и передал контроль управления коду в загрузочном секторе, начинается выполнение [boot.img](http://git.savannah.gnu.org/gitweb/?p=grub.git;a=blob;f=grub-core/boot/i386/pc/boot.S;hb=HEAD). Этот код очень простой в связи с ограниченным количеством свободного пространства и содержит указатель, который используется для перехода к основному образу GRUB 2. Основной образ начинается с [diskboot.img](http://git.savannah.gnu.org/gitweb/?p=grub.git;a=blob;f=grub-core/boot/i386/pc/diskboot.S;hb=HEAD), который обычно располагается сразу после первого сектора в неиспользуемой области перед первым разделом. Приведённый выше код загружает оставшуюся часть основного образа, который содержит ядро и драйверы GRUB 2 для управления файловой системой. После загрузки остальной части основного образа, код выполняет функция [grub_main](http://git.savannah.gnu.org/gitweb/?p=grub.git;a=blob;f=grub-core/kern/main.c). +Теперь, когда BIOS выбрал загрузочное устройство и передал контроль управления коду в загрузочном секторе, начинается выполнение [boot.img](https://git.savannah.gnu.org/gitweb/?p=grub.git;a=blob;f=grub-core/boot/i386/pc/boot.S;hb=HEAD). Этот код очень простой в связи с ограниченным количеством свободного пространства и содержит указатель, который используется для перехода к основному образу GRUB 2. Основной образ начинается с [diskboot.img](https://git.savannah.gnu.org/gitweb/?p=grub.git;a=blob;f=grub-core/boot/i386/pc/diskboot.S;hb=HEAD), который обычно располагается сразу после первого сектора в неиспользуемой области перед первым разделом. Приведённый выше код загружает оставшуюся часть основного образа, который содержит ядро и драйверы GRUB 2 для управления файловой системой. После загрузки остальной части основного образа, код выполняет функция [grub_main](https://git.savannah.gnu.org/gitweb/?p=grub.git;a=blob;f=grub-core/kern/main.c). Функция `grub_main` инициализирует консоль, получает базовый адрес для модулей, устанавливает корневое устройство, загружает/обрабатывает файл настроек grub, загружает модули и т.д. В конце выполнения, `grub_main` переводит grub обратно в нормальный режим. Функция `grub_normal_execute` (из `grub-core/normal/main.c`) завершает последние приготовления и отображает меню выбора операционной системы. Когда мы выбираем один из пунктов меню, запускается функция `grub_menu_execute_entry`, которая в свою очередь запускает команду grub `boot`, загружающую выбранную операционную систему. @@ -482,14 +484,14 @@ _start: Ссылки -------------------------------------------------------------------------------- - * [Справочник программиста Intel 80386 1986](http://css.csail.mit.edu/6.858/2014/readings/i386.pdf) + * [Справочник программиста Intel 80386 1986](https://css.csail.mit.edu/6.858/2014/readings/i386.pdf) * [Минимальный загрузчик для архитектуры Intel®](https://www.cs.cmu.edu/~410/doc/minimal_boot.pdf) - * [8086](http://en.wikipedia.org/wiki/Intel_8086) - * [80386](http://en.wikipedia.org/wiki/Intel_80386) - * [Вектор прерываний](http://en.wikipedia.org/wiki/Reset_vector) - * [Режим реальных адресов](http://en.wikipedia.org/wiki/Real_mode) + * [8086](https://en.wikipedia.org/wiki/Intel_8086) + * [80386](https://en.wikipedia.org/wiki/Intel_80386) + * [Вектор прерываний](https://en.wikipedia.org/wiki/Reset_vector) + * [Режим реальных адресов](https://en.wikipedia.org/wiki/Real_mode) * [Протокол загрузки ядра Linux](https://www.kernel.org/doc/Documentation/x86/boot.txt) - * [Справочник разработчика CoreBoot](http://www.coreboot.org/Developer_Manual) + * [Справочник разработчика CoreBoot](https://www.coreboot.org/Developer_Manual) * [Список прерываний Ральфа Брауна](http://www.ctyme.com/intr/int.htm) - * [Источник питания](http://en.wikipedia.org/wiki/Power_supply) - * [Сигнал "Питание в норме"](http://en.wikipedia.org/wiki/Power_good_signal) + * [Источник питания](https://en.wikipedia.org/wiki/Power_supply) + * [Сигнал "Питание в норме"](https://en.wikipedia.org/wiki/Power_good_signal)