Як запустити програму без операційної системи частина 6
У п'ятій частині нашої серії статей ми показали, як можна використовувати переривання BIOS після переходу в захищений режим, і як приклад визначили розмір оперативної пам'яті. Сьогодні ми розвинемо цей успіх і реалізуємо повноцінну підтримку роботи з дисками з файловою системою FAT16 та FAT32. Роботу з файлами на диску можна розбити на 2 частини: робота з файловою системою та робота з диском на рівні читання/запису секторів. Можна сказати, що для цього нам потрібно написати "драйвер" файлової системи та "драйвер" диска.
Робота з диском на рівні читання/запису секторів
Для початку навчимося працювати з диском. Отже, ми можемо викликати переривання BIOS'а. Крім інших можливостей, BIOS надає інтерфейс для роботи з диском, а саме переривання int 0x13. Зі списком сервісів, що надаються перериванням, можна ознайомитись на вікіпедії. Нас цікавлять сервіси читання та запису дисків.

Переривання повертає такі значення:
Один із параметрів – номер диска. Потрібно якось дізнатися номер диска, з яким ми збиралися працювати. Нумерація відбувається так: флоппі-диски (fdd), і все, що емулюється як флоппі, нумеруються з нуля, а жорсткі диски (hdd), і все, що емулюється як вони (usb-флешки, наприклад), нумеруються з 0x80. Цей номер не пов'язаний із послідовністю завантаження в налаштуваннях BIOS'а. У нашому випадку, диск, з яким ми збираємося працювати, є диском, з якого ми завантажилися.
Відразу після передачі управління від GRUB'а до ОС у регістрі EBX знаходиться покажчик на цю структуру. Перше поле структури – це flags, і якщо у ньому виставлено 2-й біт, то поле boot_deviceкоректно. Це поле також належить структурі Multiboot information і в його старшому байті (розмір поля - 4 байти) зберігається потрібний нам номер диска, який розуміє переривання int0x13. Таким чином, використовуючи GRUB, ми отримали недостатній параметр для читання/запису секторів на диск.

Отже, ми маємо механізм для читання сектора з диска і знання про розташування потрібного нам розділу на диску. Залишилося навчитися працювати з файловою системою у цьому розділі.
Робота з файловою системою
Для роботи з файловою системою ми використовуватимемо бібліотеку fat_io_lib. Бібліотека доступна за ліцензією GPL. Вона надає інтерфейс для роботи з файлами та директоріями, аналогічний наявному в libc. Реалізовані такі функції, як fopen(), fgets(), fputc(), fread(), fwrite() тощо. Бібліотека для своєї роботи вимагає лише дві функції: записати сектор і прочитати сектор, причому перша є необов'язковою. Функції мають наступний прототип:
Бібліотека написана на чистому С, що знову нам на руку. Для використання у своїй міні-ОС нам не доведеться змінювати в ній жодного рядка. Бібліотека очікує, що читання секторів відбувається у рамках розділу із файловою системою.
Отже, ми маємо функції читання/запису сектора на розділ і є бібліотека для роботи з FAT16/32, яка використовує ці функції. Залишилося зібрати все воєдино та продемонструвати результат. Але перш ніж перейти до коду, хотілося б показати, що підхід, який ми збираємося використати, цілком застосовний у реальному житті. Нижче представлена невелика частина VBR windows 7, в якій відбувається читання сектора диска за допомогою переривання int0x13. Цей код часто викликається в процесі завантаження системи, аж до моменту відтворення завантажувальної анімації.

! ВАЖЛИВО!Всі подальші дії можуть успішно здійснюватися тільки після успішного проходження всіх кроків із п'ятої частини нашої серії статей
Крок 1. Змінимо основну логіку kernel.c
Код, що друкує розмір оперативної пам'яті
замінимо на наступний код:
Пам'ять під змінні mbd і magic зарезервована у файлі loader.s, так що їх можна використовувати аналогічно глобальним змінним з коду С. Змінна magic містить сигнатуру, що підтверджує, що для завантаження використовувався стандарт Multiboot, еталонною реалізацією якого є GRUB. Змінна mbd вказує на структуру multiboot_info_t, яка оголошена multiboot.h. Номер завантажувального диска визначається наступним виразом: p_multiboot_info->boot_device >> 24. Функція InitBootMedia запам'ятовує номер диска і шукає перший сектор файлової системи, щоб потім усі зміщення рахувати від нього.
Бібліотека fat_io_lib для ініціалізації потребує двох функцій: fl_init і fl_attach_media. Перша функція обнуляє внутрішні структури бібліотеки, а друга отримує як параметри функції читання та запису секторів на диск, які потім використовуються для звернення до файлів. Далі йде демонстрація роботи з бібліотекою: виводиться список файлів у папці /boot/grub та друкується вміст файлу menu.lst.
2. Додаємо файл multiboot.h до папки include. Вміст файлу беремо із сайту специфікації попередньої версії.
Крок 2. Додамо функції для роботи з диском
1. До файлу include\callrealmode.h додамо прототипи наступних функцій:
2. У файлі include\callrealmode_asm.h додамо в enum callrealmode_Func нове значення так, щоб вийшло таке:
Додамо в union всередині структури callrealmode_Data щойнооголошену структуру callrealmode_read_disk. Повинно вийти таке:
3. У файл include\string.h додамо функції strncmp та strncpy, які використовуються у бібліотеці fat_io_lib.
І кілька функцій:
Функції ReadBootMedia та WriteBootMedia використовуються бібліотекою fat_io_lib для читання/запису секторів. Функція WriteBootMedia не є обов'язковою і є заглушкою, тому що в даному прикладі немає запису на диск. Її реалізація виглядала б аналогічно до функції ReadBootMedia. Функція ReadBootMedia схожа на функцію GetRamsize з попередньої статті з точністю до типу param.func, а замість param.getsysmemmap використовується param.readdisk. Функція InitBootMedia має бути викликана раніше двох інших, оскільки вона ініціалізує значення g_BootPartitionStart та g_BootDeviceInt13Num.
5. Змінимо callrealmode_asm.s. Додамо ще один тип CALLREALMODE_FUNC_READ_DISK функцій, що викликаються, має вийти наступне:
Далі додамо ще одну перевірку на тип функції та безпосередньо код, що читає з диска. Повинно вийти таке:
Мітка readdisk вказує на код, який формує структуру DAP із структури callrealmode_Data і викликає int0x13. У коді після мітки callrealmode_switch додалося 2 інструкції, що перевіряють, чи не потрібно викликати readdisk.
6. Додамо файл include\mbr.h, що містить визначення для роботи з MBR. Його вміст:
Структура MBRSector використовується у функції InitBootMedia.
Крок 3. Додамо бібліотеку fat_io_lib та запустимо
1. Завантажуємо архів fat_io_lib.zip і розпакуємо його до папки fat_io_lib в корені проекту. 2. Додамо порожні файли assert.h та stdlib.h до папки include. Вони потрібні, щоб бібліотека скомпілювалася. 3. Виправимо Makefile. Додамо файли з бібліотеки до списку цілей для компіляції. Повинновийти таке:
Тепер розмір образу дорівнює 10Mb. Це робиться для того, щоб команда mkdosfs відформатувала розділ FAT16 замість FAT12. FAT12 не підтримується бібліотекою fat_io_lib.
З цим дефайном бібліотека не включатиме stdio.h, але використовуватиме готовий прототип функції printf, який збігається з нашим, і який вже реалізований.
4. Перезберемо проект
sudo make image
Повинно вийти таке:

Як і в попередніх частинах, можна зробити dd образу hdd.img на флешку та перевірити код на реальному залізі, завантажившись із неї.