Низкоуровневый АД: пишем свою ОС — Часть 1. Загрузчик и стартовое ядро
Всем здрасте, и сегодня мы начнем наше прохождение через низкоуровневый кодинг - написание ОС. Сегодня мы напишем загрузчик (точнее конфиг к GRUB) и простенькое ядро, которое будет выводить "Hello OSDev!"
Что нам понадобится:
- Linux (у меня Kali Linux 2025.1a)
- i686-elf-gcc и i686-elf-ld (тык)
- qemu-system-i386
- nasm
- grub-mkrescue
Шаг 1. Структура папок
Создадим несколько папок:
Шаг 2. Загрузчик
Тут все просто - мы будем использовать GRUB. Но нам будет нужно написать к нему конфиг, чтобы он смог загружать именно наш загрузчик. Он будет лежать в iso/boot/grub .Конфиг простой:
В 4 строчке "Habr OS" - название нашей ОС, которая будет отображаться в меню выбора GRUB. Можете выбрать его сами (Но берите ТОЛЬКО английские буквы)
А теперь и сам наш загрузчик:
В нем мы в основном указываем GDT
Шаг 3. Ядро
Мы подошли к самому интересному - ядру. Создадим в папке kernel файл kernel.c. И тут мы столкнемся с тем, что использовать stdlib... нельзя! Потому что мы используем кросс-компилятор и библиотек он таких не знает. И тем более для каждой ОС она своя.
Давайте опишем сначала все для Multiboot (то есть для GRUB)
Для GRUB - макросы с флагами и адресом загрузки после GRUB и структура с этими данными
Дальше - стек
Тут мы описываем массив символов, который является стеком, и указатель на символ, идущий после стека
Далее у нас идет основная функция ядра - _start()
Мы пишем через ассемблеровую вставку, что куча свободной памяти начинается с того места, где закончился стек. Далее - создаем переменную с адресом VGA-видеопамяти, создаем переменную с сообщением... Стоп. Зачем так много пробелов?? Это для переноса строки, ведь \n, \t и \v и т. п. НЕ работают в консоли (пока что). В цикле for мы выводим символ, причем всегда четный элемент видеопамяти (вкл. 0) - сам символ, а нечетным - его цвет (0x07 - светло-серый на черном). А цикл while позволяет не уйти процессору в дали дальние и выйти из памяти ядра.
Шаг 4. Сборка, запуск
Ах да, мы забыли про linker.ld и сборочный скрипт! Вот linker.ld (он в boot лежит):
Тут отмечается точка старта программы.
Ну а теперь сборка! Для этого я использую спец. скрипт build.sh. Вот его внутренности:
Первым делом мы собираем наш пользовательский загрузчик. Далее мы собираем ядро для bare metal и без подключения sdtlib. Дальше линкуем пользовательский загрузчик с ядром, копируем в папку где генрируется ISO и генерируем сам ISO. Дальше можно уже засунуть его в QEMU (команда qemu-system-i386 -cdrom bin/os.iso -machine pc -d int -D qemu.log) и посмотреть на те самые строчки, которые мы ждали...
Ну а далее на нашу ОС есть планы:
1. Реализуем ввод
2. Сделаем простые команды (типа shutdown, echo)
Спасибо что прочитали статью! Буду писать продолжение
(примечание: это копия пока что неопубликованной моей статьи на хабре)