Подведу некоторый итог процессу загрузки. В предыдущих выпусках я уже писал
про файловые системы, про форматы файлов, и про защищенный режим тоже писал.
Сейчас я это немного обобщу.
То, что я до сих пор сделал пока рассчитано только на работы с дисками
1,4Мб, то есть с флопами. Это конечно ограничение в некоторой степени, но
пока система еще далеко не готова, этого достаточно. Естественно это еще не
окончательный вариант. Да и можно ли говорить об окончательности программных
продуктов? Нет предела совершенству. :)
В обязанности бутсектора входит следующее:
Если с первым и двумя последними пунктами все просто и компактно, то второй
и третий пункт требуют возможности работы с файловой системой, а третий
пункт помимо этого должен знать структуру бинарных форматов. На все это не
хватает 512 байт, отводимых для бутсектора. Наш бутсектор занимает больше -
один килобайт.
В файловой системе EXT2 с этим не возникает никаких проблем, поскольку
первый килобайт файловой системы не используется.
В FAT это немного сложнее. Служебная структура, именуемая Boot Sector
Record (BSR), содержит в себе все необходимые поля для выделения для
загрузочного сектора места более чем 512 байт. Но как это сделать при
форматировании, стандартными средствами, я не нашел. И если формат диска не
соответствует каким-то внутренним представлениям Windows, то содержимое
такого нестандартного диска может быть испорчено. Выход был найден случайно.
Как оказалось утилита format хоть и не имеет таких параметров командной
строки, но перед форматированием берет информацию из BSR. И если
предварительно заполнить эту структуру (с нужными нам параметрами), а потом
уже форматировать, то все получается так, как хочется нам. Таким образом, у
меня получилось сделать диск, у которого два сектора зарезервированы (там
будет размещаться boot), и одна копия FAT.
Ну теперь давайте по порядку рассмотрим все этапы работы бутсектора.
Бутсектор загружается БИОСом по адресу 0:7c00h занимает он 512 байт. Память
начиная с адреса 0:7e00h свободна. но в эту память мы загрузим второй
сектор бута. Одновременно загружается информация необходимая для
обслуживания файловой системы. Для EXT2 дополнительно необходимо загрузить
два килобайта (суперблок и дескрипторы групп), для FAT немного больше -
4,5 килобайта (первая копия FAT).
mov ax, 0x7e0
mov es, ax
Адрес 0:7e00h идентичен адресу 7e0h:0. Вторым вариантом мы и будем
пользоваться, потому что наша процедура загрузки секторов размещает их по
сегментному адресу, хранящемуся в es.
mov ax, 1
В ax номер сектора, с которого начинается чтение (первый сектор является нулевым (каламбур :). И далее все зависит от файловой системы.
%ifdef EXT2FS
mov cx, 5
Для EXT2 загружается 5 секторов - второй сектор бутсектора (1 сектор),
суперблок файловой системы (2 сектора) и дескрипторы групп (2 сектора).
%elifdef FATFS
mov cx, 10
Для FAT загружается 10 секторов - второй сектор бутсектора (1 сектор),
таблица FAT - 9 секторов (такой размер она имеет на floppy дисках).
%else
%error File system not specified
%endif
call load_block
Все. первый пункт загрузки выполнен.
Функции обслуживания файловых систем имеют одинаковый интерфейс. Cобственно
их всего две fs_init и fs_load_file. Естественно у них различаются
реализации, но в процессе компиляции выбирается используемая файловая
система. Для совместного использования нам никак не хватит одного килобайта,
да и не за чем это.
Из-за сложности VFAT (FAT с длинными именами) он не реализован. Все
имена на диске FAT должна иметь формат 8.3
В файловой системе FAT я не оперирую принятыми в MS системах именами дисков
и при указании пути использую путь относительно корневой директории диска
(как это делается в юникс системах).
Файл конфигурации у нас пока называется boot.rc и находится в каталоге /etc.
Формат у этого файла достаточно нестрогий. Из-за нехватки места в boot
секторе там сделана реакция только на ключевые слова, которыми являются:
Предварительно проинициализировав файловую систему
call fs_init
Мы загружаем этот файл с диска.
mov si, boot_config
call fs_load_file
...
boot_config:
db '/etc/boot.rc', 0
Содержимое файла конфигурации такое:
kernel /boot/kernel
#end
Модулей у нас пока никаких нет, да и ядро еще в зачаточном состоянии. Но
речь сейчас не об этом.
Про этот момент я не буду особо расписывать, желающие могут посмотреть в
исходниках, которые в скором времени появятся на сайте.
Скажу только, что программа анализирует файл конфигурации, в соответствии с
ключевыми словами загружает ядро (которое может быть в единственном
экземпляре) и любое количество модулей. Общий объем ядра и модулей ограничен
свободным размером базовой памяти (около 600к).
Перейдем к предпоследнему пункту.
Бутсектор не особо беспокоится об организации памяти в системе - это забота
ядра. Для перехода в защищенный режим он описывает всего два сегмента:
сегмент кода и сегмент данных. оба сегмента имеют базовый адрес - 0 и
предел в 4 гигабайта (это нам пригодиться для проверки наличия памяти).
Перед переходом в защищенный режим нам необходимо включить адресную линию
A20. По моим сведениям этот механизм ввели в пору 286 для предотвращения
несанкционированных обращений к памяти свыше одного мегабайта (непонятно
зачем?). Но поскольку это имеет место быть - нам это нужно обрабатывать,
иначе каждый второй мегабайт будет недоступен. Делается это почему-то через
контроллер клавиатуры (еще одна загадка).
mov al, 0xd1
out 0x64, al
mov al, 0xdf
out 0x60, al
После этого можно переходить в защищенный режим.
lgdt [gd_desc]
В регистр gdtr загружается дескриптор GDT.
push byte 2
popf
Очищается регистр флагов.
mov eax, cr0
or al, 1
mov cr0, eax
Включается защищенный режим. Не смотря на то, что процессор уже находится в
защищенном режиме, мы пока работаем в старых адресах. Чтобы от них уйти нам
нужно сделать дальный переход в адреса защищенного режима.
jmp 16:.epm
BITS 32
.epm:
16 в этом адресе перехода - это не сегмент. Это селектор сегмента кода.
mov ax, 8
mov ds, ax
mov es, ax
; Ставим стек.
mov ss, ax
movzx esp, sp
После всего этого мы инициализируем сегментные регистры соответствующими
селекторами, в том числе и сегмент стека, но указатель стека у нас не
меняется, только теперь он становится 32-х битным.
...
gd_table:
; пеpвый дескpиптоp - данные и стек
istruc descriptor
at descriptor.limit_0_15, dw 0xffff
at descriptor.base_0_15, dw 0
at descriptor.base_16_23, db 0
at descriptor.access, db 0x92
at descriptor.limit_16_19_a, db 0xcf
at descriptor.base_24_31, db 0
iend
; втоpой дескpиптоp - код
istruc descriptor
at descriptor.limit_0_15, dw 0xffff
at descriptor.base_0_15, dw 0
at descriptor.base_16_23, db 0
at descriptor.access, db 0x9a ; 0x98
at descriptor.limit_16_19_a, db 0xcf
at descriptor.base_24_31, db 0
iend
Это GDT - Глобальная таблица дескрипторов. Здесь всего два дескриптора, но
во избежание ошибок в адресации обычно вводится еще один дескриптор -
нулевой, который не считается допустимым для использования. Мы не будем
резервировать для него место специально, просто начало таблицы сместим на
8 байт выше.
gd_desc:
dw 3 * descriptor_size - 1
dd gd_table - descriptor_size
А это содержимое регистра GDTR. Здесь устанавливается предел и базовый
адрес дескриптора. обратите внимание на базовый адрес, здесь происходит
резервирование нулевого дескриптора.
Теперь процессор находится в защищенном режиме и уже не оперирует
сегментами, а оперирует селекторами. Селекторов у нас всего три.
Нулевой - недопустим. восьмой является селектором данных и шестнадцатый -
селектором кода.
После этого управление можно передать ядру. дальше со всем этим будет
разбираться оно.
Здесь вообще все просто. Когда мы загрузили ядро, в файле ядра мы
определили адреса сегмента кода и сегмента данных. Не смотря на то, что ядро
имеет вполне конкретные смещения в сегменте (которые задаются при
компиляции), код инициализации ядра рассчитан на работу без привязки к
адресам. Это нужно для определения количества памяти, после перевода ядра
на свои адреса доступ ко всей памяти будет для ядра затруднен в связи с
включением механизма страничного преобразования.
Итак, переходим к выполнению кода ядра.
mov ebx, kernel_data
mov eax, [ebx + module_struct.code_start]
jmp eax
В этом фрагменте в eax записывается адрес начала кодового сегмента ядра.
Так как сегмент кода у нас занимает всю виртуальную память, нам не важно
где находится ядро (хотя мы знаем, что оно было загружено в базовую память).
Мы просто передаем ему управление.
О ядре мы начнем говорить в следующем выпуске. С загрузчиком мы практически
закончили. Он уже обладает достаточной для нас функциональностью.
Как всегда вы можете задавать свои вопросы, высказывать пожелания по
адресу, указанному ниже.
Хочу принести свои извинения читателям рассылки в текстовом формате. Что-то
на субскрайбе с конвертацией не то.
Отправлено 2001-11-29 для 5592 подписчиков.
ведущий рассылки Dron
Сайт проекта
Архив Рассылки
При поддержке Kalashnikoff.ru
| (C)Москва, 2001. Авторское право принадлежит Валяеву А.Ю. Публичное размещение материала из рассылки, а также его использование полностью или частично в коммерческих или иных подобных целях без письменного согласия автора влечет ответственность за нарушение авторских прав. |
| http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |