Написание ОС с нуля: Часть 2 — 32 лучше 16-ти

от автора

Дисклеймер?

Хай Хабр! Это серия статей по написанию моей ОС с нуля. Я лютый фанат ретропрограммирования, поэтому я мгновенно забуду про существование EDК. Просьба не писать комменты по типу «BIOS давно устарела где UEFI?». Пишу это просто чтобы было, что почитать вечером и порелаксить. Спасибо.

Давайте договоримся

Если вы не владеете языком ассемблера, то можете испытать сложности в понимании происходящего. Пользуюсь я ассемблером FASM

В прошлой части мы написали собственный загрузчик из двух стадий: ограниченой рамками MBR и без ограничений(ну, почти), которая даже имеет красивый колхозный BSOD. В этой части будем переходить в защищенный режим.

BSOD(да, здесь я попробовал запустить это чудо в Oracle VMs VirtualBox)
BSOD(да, здесь я попробовал запустить это чудо в Oracle VMs VirtualBox)

Проблемы х86-16

Ну, думаю здесь можно долгий список писать. Выделю, как по мне, самые основные:

1. ограничение адресации ОЗУ в 64Кб

2. 0 защиты и изоляции памяти

3. Отсутствие некоторых возможностей. Думаю, и здесь можно понять, что IA-32 лучше чем х86-16 как архитектура.

Выйди за меня, OSDev.org!

Да, да-а… Этот самый сайт для медитации в момент brainkudatoushel’. Я изучил страницы осдева, посвященные защищенному режиму вдоль и поперек, и уже начинал заталкивать в голову монитор. Через пару часов, не без помощи Макса, я все понял и начал писать.

Что такое GDT и с чем его едят

Итак, реальный режим плохой, есть более удобный вариант — защищенный. В нем память адресуется линейно, все 4ГБ адресов, помещающихся в регистр x86 процессора. Но за годы работы в реальном режиме уже выяснили, что адресовать физическую память напрямую очень неудобно (как минимум, возникает вопрос загрузки в неё нескольких процессов так, чтобы они не конфликтовали), небезопасно (процессы могут нарушать память друг друга), да и вообще памяти может быть меньше 4ГБ и было бы удобно назначать разным участкам виртуальной памяти разные права (Read, Write, Execute или ничего). Поэтому придумали страничную адресацию и виртуальную память.

Каждый процесс имеет свои виртуальные 4ГБ, а процессор соотносит виртуальный адрес с реальным, проверяя, заодно, права на запрошенную операцию. Для этого виртуальная память делится на страницы. Страница — как сегмент, но она может начинаться в произвольном месте физической памяти, иметь произвольный размер, настройки доступа и предназначение. Описание страницы, по-английски — «дескриптор» содержит все эти свойства. А список всех страниц, соответственно, будет называться таблицей дескрипторов. Почему таблицей? Потому, что для доступа к разным страницам можно будет просто указать номер дескриптора нужной страницы в сегментном регистре (теперь это «селектор»).

Было бы удобно иметь глобальную таблицу десткрипотов, одну на всю систему, для доступа к разделяемым библиотекам и для целей ядра системы, и откальные — по одной на каждый поток/процесс. Называются они, соответственно, Global Descriptor Table и Local Descriptor Table.

Ну вот. Итак, нам нужен «описыватель» для кода и данных. Всю таблицу можно найти под спойлером с отрывком 1.

Дресс-код

kernel.asm (отрывок 1)
GDT: ; нулевой дескриптор. просто надо. dw 0     .size dw @f-GDT-1 ; размер     .linear dd GDT ; адрес     .code = $ - GDT ; дескриптор кода         dw -1,0         db 0,9ah,0cfh,0     .data = $ - GDT ; дескриптор данных         dw -1,0         db 0,92h,0cfh,0     .pointer: ; указатель         dw GDT.size         dd GDT     @@:

В принципе, этот код можно взять за шаблон и использовать везде, он практически универсален.

kernel.asm (другой отрывок)
cli                     ; чтобы никто не отвлекал lgdt fword[GDT.pointer] ; Загрузить GDT mov eax, cr0            ; получить контент регистра СR0 or al, 1                ; установить бит РЕ mov cr0,eax             ; применить изменения jmp GDT.code:pmode      ; прыжок, просто надо  pmode:  use32 ; генерировать 32-битный машинный код  ; здесь настроим сегментные регистры mov ax, GDT.data ; data segment mov ds, ax ; stack segment mov ss, ax mov ax, 0xA000 ; graphic segment ?? mov gs, ax  mov sp, 0xFFFF ; Почему бы не расположить стек в нижней памяти?  ...

Здесь мы входим в защищенный режим, подгружая в GDTR указатель на GDT и настраивая сегментные регистры.

Ну вот и все! В конец можно поставить код для зависания ЦП или свой код (демку какую-нибудь, например).

Вот, например, что сделал я:

Просто слеш. скуууушно
Просто слеш. скуууушно

Спасибо за внимание!

П.Ы.: Принимаю любые предложения по развитию проекта или статей в лс Хабра

Ссылки:

  1. HexOS-GitHub

  2. Предыдущая часть

  3. Здесь будет ссылка на следующую часть


ссылка на оригинал статьи https://habr.com/ru/post/670162/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *