Как да стартирате програма без операционна система, част 6
В петата част от нашата поредица от статии показахме как можете да използвате прекъсванията на BIOS след влизане в защитен режим и определихме размера на RAM като пример. Днес ще надградим този успех и ще внедрим пълна поддръжка за работа с дискове с файлови системи FAT16 и FAT32. Работата с файлове на диска може да бъде разделена на 2 части: работа с файловата система и работа с диска на ниво сектори за четене / запис. Можем да кажем, че за това трябва да напишем "драйвер" за файлова система и "драйвер" за диск.
Работа с диска на ниво сектори за четене/запис
Първо, нека се научим как да работим с диска. За да можем да задействаме прекъсвания на BIOS. Освен всичко друго, BIOS предоставя интерфейс за работа с диска, а именно прекъсването int 0x13. Списък на услугите, предоставяни от прекъсването, можете да намерите в Wikipedia. Ние се интересуваме от услуги за четене и запис на дискове.
Прекъсването връща следните стойности:
Един от параметрите е номерът на диска. Трябва по някакъв начин да разберем номера на диска, с който ще работим. Номерацията е следната: флопи дисковете (fdd) и всичко, което се емулира като флопи, се номерират от нула, а твърдите дискове (hdd) и всичко, което се емулира като тях (usb флашки, например), се номерират от 0x80. Този номер няма нищо общо с последователността на зареждане в настройките на BIOS. В нашия случай дискът, с който ще работим, е дискът, от който стартирахме.
Веднага след прехвърлянето на управлението от GRUB към ОС, EBX регистърът съдържа указател към тази структура. Първото поле на структурата е флагове и ако в него е зададен 2-ри бит, тогава полето boot_deviceправилно. Това поле също принадлежи към информационната структура Multiboot и неговият старши байт (размерът на полето е 4 байта) съхранява номера на диска, от който се нуждаем, който разбира прекъсването int0x13. По този начин, използвайки GRUB, получихме липсващата опция за четене / запис на сектори на диск.
И така, имаме механизъм за четене на сектор от диск и информация за местоположението на дяла, от който се нуждаем на диска. Остава да научите как да работите с файловата система на този дял.
Работа с файловата система
За да работим с файловата система, ще използваме библиотеката fat_io_lib. Библиотеката е достъпна под GPL лиценз. Предоставя интерфейс за работа с файлове и директории, подобен на този на libc. Внедрени функции като fopen(), fgets(), fputc(), fread(), fwrite() и др. Библиотеката за своята работа изисква само две функции: писане на сектор и четене на сектор, като първата е незадължителна. Функциите имат следния прототип:
Библиотеката е написана на чисто C, което отново играе в нашите ръце. За да го използваме в нашата мини-ОС, не е нужно да променяме нито един ред в него. Библиотеката очаква секторите да се четат в рамките на дял с файлова система.
И така, имаме функциите за четене / запис на сектор в дял и има библиотека за работа с FAT16 / 32, която използва тези функции. Остава да съберем всичко и да демонстрираме резултата. Но преди да премина към кода, бих искал да покажа, че подходът, който ще използваме, е доста приложим в реалния живот. По-долу е малка част от VBR Windows 7, в която дисковият сектор се чете с помощта на прекъсването int0x13. Този код се извиква многократно по време на процеса на зареждане на системата, до момента, в който се изчертае анимацията за зареждане.
! ВАЖНО!Всички по-нататъшни действия могат да бъдат извършени успешно само след успешно завършване на всички стъпки от петата част на нашата поредица от статии
Стъпка 1. Променете основната логика в kernel.c
Код, който отпечатва размера на RAM
замени със следния код:
Паметта за променливите mbd и magic е запазена във файла loader.s, така че да могат да се използват по същия начин като глобалните променливи от кода на C. Променливата 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 към включената папка. Взимаме съдържанието на файла от сайта със спецификации на предишната версия.
Стъпка 2. Добавете функции за работа с диска
1. Добавете прототипите на следните функции към файла include\callrealmode.h:
2. Във файла include\callrealmode_asm.h добавете нова стойност към enum callrealmode_Func, така че да получим следното:
Нека добавим към обединението в структурата callrealmode_Data току-щодекларирана структура callrealmode_read_disk. Трябва да получите следното:
3. Добавете функциите strncmp и strncpy, използвани в библиотеката fat_io_lib към файла include\string.h.
И няколко функции:
Функциите ReadBootMedia и WriteBootMedia се използват от библиотеката fat_io_lib за четене/запис на сектори. Функцията WriteBootMedia не е задължителна и е мъниче, защото в този пример няма запис на диск. Реализацията му ще изглежда подобно на функцията ReadBootMedia. Функцията ReadBootMedia е подобна на функцията GetRamsize от предишната статия до типа param.func и param.readdisk се използва вместо param.getsysmemmap. Функцията 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 към папката за включване. Те са необходими за компилирането на библиотеката. 3. Нека поправим Makefile. Нека добавим файлове от библиотеката към списъка с цели за компилация. Трябва давземете следното:
Сега размерът на изображението е 10Mb. Това е така, че командата mkdosfs ще форматира дяла във FAT16 вместо FAT12. FAT12 не се поддържа от fat_io_lib.
С това по подразбиране библиотеката няма да включва stdio.h, а ще използва готов прототип на функция printf, който е същият като нашия и който вече е имплементиран.
4. Преизградете проекта
sudo създава изображение
Трябва да получите следното:
Както в предишните части, можете да добавите изображението hdd.img към флаш устройство и да тествате кода на реален хардуер, като стартирате от него.