Дисклеймер?

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

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

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

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

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

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

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

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

  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. Здесь будет ссылка на следующую часть.

Комментарии (10)


  1. titbit
    07.06.2022 19:03
    +5

    IA-16 — это что именно имеется ввиду?

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

    В 8086 было 20 адресных линий (до 1 мб), в 80286 — 24 (до 16мб). 320Кб не было ни у кого.

    При всем уважении к работе, хотелось бы больше прочитать про «почему вы выбрали такие решения»? Почему именно такие дескрипторы в GDT? Где IDT? Как вы определяете размер памяти (ну где стек размещать)? Ну и т.д.


  1. 13werwolf13
    07.06.2022 19:43
    +6

    такое ощущение что прочитал не статью а первый абзац статьи.. ну или шаблон.. зря только бутербродов наготовил..


  1. 4eyes
    07.06.2022 20:26
    +2

    Листинги хорошие, а статья - не знаю, т.к. статьи нет.

    Вот написали вы про недостатки "IA-16", точнее про недостатки реального режима работы процессора. И дальше отрывок из Википедии. Извините за такой вопрос, но почему там отрывок про GDT, а не ВУРС, к примеру? Представьте, что вашу статью, соверненно неожиданно, читает человек, который не знает, что такое GDT и зачем нужны дескрипторы.

    Вернемся к написанию статьи:

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

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

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

    И дальше можно писать про GDT, но ради бога, не расшифровку аббревиатуры с Википедии, а что-нибудь интересное или имеющее практическую пользу.


    1. TalismanChet Автор
      07.06.2022 20:38

      Спасибо за замечание! Просто делалась статья "на коленке", так что возможны недочеты. Думаю, вы не будете против, что отрывок из вашего комментария попадет в статью слово-в-слово?


      1. 4eyes
        07.06.2022 20:47

        Без проблем.


  1. 4eyes
    07.06.2022 20:56
    +2

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

    Бесспорно и совершенно очевидно. Из-за отсутствия некоторых возможностей в реальном режиме работы процессора, реальный хуже защищенного во всех случаях, кроме тех, когда не хуже.

    Анекдот

    Спрашивает грузин у армянина:

    — Кто лучше, грузины или армяне?

    — Армяне.

    — А чем лучше?

    — Чем грузины.


  1. Kotofay
    07.06.2022 21:26

    Есть прекрасные переводы помогающие понять кухню виртуальных адресов
    https://habr.com/ru/post/445618/
    https://habr.com/ru/post/436606/
    И работающий long mode https://wiki.osdev.org/Entering_Long_Mode_Directly


    1. TalismanChet Автор
      08.06.2022 11:46

      Спасибо) Может, я воспользуюсь информацией из указанных вами источников.


  1. gdt
    08.06.2022 08:32

    Интересная идея в 2022, я помню в 2010 хотел сделать свою ОС с экзоядром, барышнями и преферансом (ник кстати тоже оттуда) - и уже тогда всё это было немножко не на острие прогресса так сказать. Вам конечно желаю успехов и настойчивости!


    1. TalismanChet Автор
      08.06.2022 11:45

      Спасибо большое :)