«Низкоуровневое программирование полезно для души программиста».
- Джон Кармак

Привет всем, читающим это. Поскольку технологии вокруг нас развиваются ежедневно, мы иногда забываем оценить красоту самых основных основ, на которых построены наши компьютеры. Итак, сегодня в этой статье я постараюсь максимально просто объяснить, что такое загрузчик и как собрать базовый. Прежде чем двигаться дальше, я хотел бы поблагодарить этот сайт за его потрясающее содержание и лаконичную подачу информации. Эта статья также во многом вдохновлена ​​этим сайтом. Я очень рекомендую прочитать статьи здесь для получения более подробной информации по этой теме. Для понимания кода в этой статье требуется базовый уровень знаний о языке ассемблера. Лучше, если у вас есть необходимые знания ассемблера, однако я постараюсь охватить большинство требуемых понятий. Итак, приступим ...

  1. Как мой компьютер запускается?

Что ж, большинство из нас могло подумать, что на самом деле происходит, когда мы нажимаем кнопку питания. В аппаратном обеспечении вашей системы есть что-то, известное как PSU (блок питания). Когда вы нажимаете кнопку питания, он отправляет сигнал на блок питания, который преобразует исходный переменный ток в постоянный и подает его на другие компоненты системы. Если он может обеспечить надлежащее питание, он отправляет в BIOS сигнал «power-good», указывающий, что вы можете взять на себя ответственность сейчас.

При получении этого сигнала BIOS запускает другой процесс, известный как POST (Power On Self Test). Это своего рода проверка того, готовы мы к работе или нет. Он проверяет наличие надлежащего источника питания или его отсутствие, не повреждена ли память, какие устройства подключены и т. Д. Если что-то не так, он сообщает об ошибке, показывая номер порта ввода / вывода или последовательность звуковых сигналов и т. Д.

Теперь, после этой последней проверки, если POST обнаруживает, что все в порядке, он показывает зеленый флаг для BIOS (просто аналогия :)). Следовательно, теперь управление передано в BIOS.

Наконец-то мы подошли к BIOS. BIOS теперь создает таблицу векторов прерываний (IVT). В этой таблице хранятся адреса различных подпрограмм обслуживания прерываний. Прерывание - это механизм, используемый оборудованием для сигнализации процессору о возникновении события.

Теперь основная задача BIOS - загрузить загрузчик. Но как мы узнаем, где он находится на нашем запоминающем устройстве?

Ответ на вышеупомянутый вопрос заключается в том, что у нас есть сектор на нашем устройстве хранения, который называется загрузочным сектором (Примечание: для получения дополнительной информации о том, что такое секторы, прочтите это). Загрузочный сектор обычно является первым сектором диска или сектором 0 на дорожке 0 на головке 0. Теперь BIOS перемещает данные из загрузочного сектора в основную память по адресу 0x7c00, и мы будем использовать прерывание 0x19 для перехода к нему.

Если предыдущий абзац был вам непонятен, не волнуйтесь. Когда мы поговорим подробнее о загрузчиках, вы, возможно, поймете это лучше.

2. Собираем необходимые вещи.

Теперь, прежде чем мы продолжим и поговорим о загрузчиках, нам нужно установить несколько вещей. Во-первых, поскольку мы собираемся написать код на языке ассемблера, нам понадобится ассемблер, чтобы преобразовать его в машинный язык. NASM - один из таких ассемблеров для архитектуры Intel x86. Во-вторых, мы установим qemu, эмулятор для тестирования нашего загрузчика, поскольку мы не хотим тестировать его непосредственно на нашем оборудовании.

Чтобы установить NASM и qemu (в ОС на базе Debian):

sudo apt-get install nasm qemu

Для других ОС вы можете обратиться в Интернет.

3. Пишем наш загрузчик ..

org 0x7c00
bits 16
Start: 
    cli
    hlt 
times 510 — ($-$$) db 0
dw 0xAA55

Код может показаться вам немного неудобным или знакомым. В любом случае давайте обсудим, что это означает, строка за строкой.

Во-первых, у нас есть org 0x7c00. Как мы уже обсуждали ранее, наш загрузчик загружается в память по адресу 0x7c00, поэтому эта строка сообщает NASM о необходимости установить все адреса в соответствии с тем фактом, что первая инструкция находится в 0x7c00.

Следующая строка сообщает нам, что мы находимся в 16-битном реальном режиме. Поскольку архитектура x86 имеет 16-битные регистры, по умолчанию мы загружаемся только в 16-битном реальном режиме. Однако мы можем переключиться в 32-битный защищенный режим, но я не буду здесь обсуждать это. Однако вы можете обратиться к this для получения дополнительной информации о переключении режимов.

Теперь cli и hlt. cli используется для очистки всех прерываний, а hlt используется для остановки системы. Это гарантирует, что ЦП не выполняет случайные инструкции за пределами нашего кода.

Теперь «раз 510 - ($ - $$) db 0». Это требует некоторого обсуждения. Поскольку мы знаем, что один сектор содержит всего 512 байт, мы не можем записать загрузчик большего размера, поскольку мы копируем с диска только один сектор (то есть загрузочный сектор). Кроме того, мы не можем иметь размер менее 512 байт, так как это может привести к случайным инструкциям или ошибкам. Итак, чтобы гарантировать, что размер нашего кода составляет 512 байт, мы заменяем любой из оставшихся байтов на 0. $ представляет адрес текущей строки, а $$ представляет адрес первой строки. Следовательно, ($ - $$) представляет размер программы. Но почему мы используем только 510, а не 512?

Мы гарантируем, что наш код имеет размер 510 байт, потому что последние байты зарезервированы для определенного использования. Последние 2 байта хранят подпись загрузки. Последняя строка гарантирует, что 0xAA хранится в 511-м байте, а 0x55 сохраняется в 512-м байте. Эти 2 байта сообщают BIOS, что этот сектор является загрузочным.

Теперь, как протестировать наш загрузчик? Очень простой.

Во-первых, нам нужно преобразовать наш код в машинный код. Просто введите эту команду (учитывая, что имя вашего файла, в который вы пишете, это myboot.asm, вы можете использовать любое имя).

nasm -f bin -o myboot.bin myboot.asm

После этого запустите команду ниже. Он копирует наш загрузчик в первый сектор образа виртуальной дискеты с помощью утилиты «dd».

dd status=noxfer conv=notrunc if=myboot.bin of=myboot.flp

Наконец, загрузите свой первый загрузчик :).

qemu-system-i386 -fda myboot.flp

Выход:

СПАСИБО ЗА ЧТЕНИЕ