gdt dq 0,0x00CF9A000000FFFF,0x00CF92000000FFFF
NULL_SELECTOR = 0; нулевой селектор считаеться некореектным вне зависимости от таблицы
CODE_SELECTOR = 1 shl 3
DATA_SELECTOR = 2 shl 3
gdtr:
.limit dw 3 * 8 - 1 ; На 1 меньше
.base dd gdt
Защищённый режим процессора
Разобрался с реальным режимом, решил перейти на защищённый. Нашёл много теории, но довольно мало практики. Переход с помощью изменения бита в cr0. По-моему нужно ещё запрет прерываний. Это первый вопрос: как точно это сделать и нужно что-либо ещё? И как работать с прерываниями(есть ли способ без перехода в реальный режим) и памятью(т.е. страничной)?
В PM (Protected Mode) в сегментных регистрах не старшая часть адреса, а селектор. Селектор - это номер дескриптора сегмента в таблице и DPL. По началу тебе не потребуеться ни локальная таблица дескрипторов, ни уровни привелегий сегментов, поэтому младшие 3 бита селектора у тебя всегда будут в нуле.
После установки бита в cr0 ты ещё не совсем в PM - код 16-битный. Для полного перехода надо совершить дальний переход на селектор сегмента, который будет описан как исполняемый. Сегменты в общем то не обязаны иметь базу ноль, но сегментация считаеться устаревшей и не рализованна нигде кроме процессоров Intel, поэтому советую использовать т. н. flat-режим. Это когда все сегменты имеют базу 0, а лимит 4 ГБ (кстати, в 64-битном режиме эти параметры вообще игнорируеться). Вот пример описания таблицы дескрипторов:
Код:
Перед пришком надо выполнить lgdt [gdtr]. После этого можно выполнить jmp 8:start32 и код будет выполняться в полноценном защищённом режиме.
Прерывания BIOS в PM недоступны. То есть никаких сервисов вроде int 0x10 или int 0x16 нет - всё надо писать самому. В случае с клавой и текстовым экраном всё достаточно просто и стандартизированно так что написать свои процедуры работы с ними не составит труда. А вот с графикой всё несколько сложнее, потому что тут единых низкоуровневых стандартов нет.
Таблица прерываний теперь тоже иная. Каждый элемент занимает 8 байт и помимо адреса там хранятся ещё и кое-какие атрибуты. Структура дескриптора такова:
Код:
offset_0_15 dw ? ; Биты 0 - 15 смещения
segment dw ? ; Селектор
reserved db ? ; Толжно быть 0
attr db ? ; Атрибуты. Для начала сойдёт 0x8E, но разобраться в них всё же стоит
offset_16_31 dw ? ; Биты 16 - 32 смещения
segment dw ? ; Селектор
reserved db ? ; Толжно быть 0
attr db ? ; Атрибуты. Для начала сойдёт 0x8E, но разобраться в них всё же стоит
offset_16_31 dw ? ; Биты 16 - 32 смещения
Надо подготовить такую таблицу где-нибудь в памяти. Потом делаем:
Код:
idtr:
.limit dw 256 * 8 - 1
.base dd 0x100000 ; Любой адрес, который вам удобен
...
lidt [idtr]
.limit dw 256 * 8 - 1
.base dd 0x100000 ; Любой адрес, который вам удобен
...
lidt [idtr]
После настройки таблицы и lidt можно разрешать прерывания с помощью sti. Однако по умолчанию прерывания будут конфликтовать с векторами исключений процессора за которыми зарезервирован диапазон от 0 до 0x20, поэтому наобходимо перенастроить контроллер прерываний. Например, на базу IRQ 0x20. Делаеться это так (заодно все IRQ от 0 до 15 будут рядом, а не разделены на 2 группы)
Код:
mov al, 0x11
out 0x21, al
out 0xA1, al
mov al, 0x20 ; База для ведущего контроллера
out 0x21, al
mov al, 0x28 ; База для ведомого контроллера
out 0xA1, al
mov al, 4
out 0x21, al
mov al, 2
out 0xA1, al
mov al, 1
out 0x21, al
out 0xA1, al
out 0x21, al
out 0xA1, al
mov al, 0x20 ; База для ведущего контроллера
out 0x21, al
mov al, 0x28 ; База для ведомого контроллера
out 0xA1, al
mov al, 4
out 0x21, al
mov al, 2
out 0xA1, al
mov al, 1
out 0x21, al
out 0xA1, al
Теперь про страничное преобразование. Есть каталог страниц. На него указывает регистр cr3. Каталог должен быть выровнен на 4 КБ, кажется. В cr3 указываеться физический адрес. Каталог занимает 4 КБ и является массивом из 1024 двойных слов. Если 0 бит установлен, то биты 12 - 31 являються физическим адресом таблицы страниц, которая по структуре аналогична каталогу, но там уже каждый элемент с 0 битом является физическим адресом страницы. Таким образом по этим таблицам можно определить соответствующий физический адрес для каждого виртуального адреса. Разумееться, не надо монтировать все страницы. Пустые пространства вполне допустимы. При обращении по адрес не сопаставленному ни одной физической странице происходит исключение (прерывание) 14. При его вызове в регистр cr2 помещаеться адрес, вызвавший ошибку, а в стек запихиваться код ошибки (разные бывают. главное не забыть его перед iret оттуда вытащить). После iret исполнение продолжается с инструкции вызвавшей ошибку. Это позволяет, например, подгрузить страницу из свопа и программа ничего не заметит.
Загрузить адрес в cr3 мало ещё надо установить бит 31 в регистре cr0.
Физические адреса в таблицах и каталогах должны быть выровнены на 4 КБ и младшие 12 бит используются по флаги. Например, бит 0 - бит присутствия страницы/каталога, бит 1 - страница доступна для записи. Биты 9 - 11 не используются процессором и могут свободно использоваться вашей программой для своих целей (то есть не зарезервированы, а именно не используются). Если быт 0 сброшен, то процессор на прочие биты и не смотрит и в них может быть что угодно. Например, смещение в файле подкачки (если вы реализовали своп).
Кстати, насчёт исключений. Если дескриптор произошедшего прерывания некорректен, то происходит General Protection Fault - 13-ое исключение. Если его дексриптор тоже некорректен или ошибка произошла в момент работы другого обработчика исключения, то происходит Double Fault (не помню какой вектор) и если его нормально тоже не обработать, то происходит сброс процессора (перезагрузка). Кстати, при некоторых обстоятельствах в стеке может оказаться некорректный адрес возврата и iret приведёт к DF.
Пока вы не начнёте разделять ядро системы и приложения (если вы ОС пишите) вам хватит сведений выше (я не говорил про уровни привилегий. Например, можно сделать так, чтобы ядро имело доступ ко всем страницам, а приложение лишь к страницам со специальным флагом. Могла делать int только с определённым номером. И прочее).