18 Июн 2026, Чт

МиМ. Защищенный режим x86

История организации памяти: http://www.uic.unn.ru/~kavs2/ZIP/AsmOS/OS/5.html

В микропроцессоре 8086 было четыре 16-разрядных сегментных регистра:

  • CS — сегментный регистр кода;
  • DS — сегментный регистр данных;
  • ES — сегментный регистр дополнительных данных;
  • SS — сегментный регистр стека.

Начиная с микропроцессора 80386, сегментных регистров стало шесть, но их разрядность не изменилась. «Новичками» стали два сегментных регистра дополнительных данных — FS и GS.

Общие правила использования сегментных регистров процессором таковы:

  • для выборки кода команды всегда используется сегментный регистр CS;
  • при обращении к стеку (смещение формируется с использованием регистров SP/ESP/RSP или BP/EBP/RBP) всегда используется сегментный регистр SS;
  • в строковых операциях при обращении к операнду-приёмнику (смещение в регистре DI/EDI/RDI) применяется сегментный регистр ES;
  • во всех остальных случаях, если не используется префикс замены сегмента, доступ к памяти осуществляется с использованием сегментного регистра DrS. При наличии префикса замены сегмента вместо DS используется указанный префиксом сегментный регистр.

В реальном режиме содержимое каждого сегментного регистра представляет собой номер параграфа — 16-байтового участка памяти, выровненного на границу 16 байт.

В защищённом режиме каждый сегментный регистр делится на три части, как показано на рисунке:

Бит TI в этом случае указывает, какая таблица дескрипторов должна использоваться: нуль соответствует таблице глобальных дескрипторов (GDT), единица — таблице локальных дескрипторов (LDT).
Поле Index является номером (индексом) дескриптора в таблице дескрипторов; этот дескриптор используется при вычислении линейного адреса.
Наконец, поле RPL является запрошенным уровнем привилегий, используемым для контроля прав доступа программы к сегменту.

Источник информации: https://osdev.fandom.com/ru/wiki/%D0%A1%D0%B5%D0%B3%D0%BC%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B5_%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D1%8B

Таблицы дескрипторов

В процессорах Intel одновременно в системе может существовать две дескрипторных таблицы: Глобальная (Global descriptor table или GDT) и Локальная (Local descriptor table или LDT).

GDT существует в единственном экземпляре. Адрес и предел GDT хранятся в специальном системном регистре (GDTR) в 48 бит длиной (6 байт).
LDT может быть индивидуальная для каждой задачи, или общая для системы, или же ее вообще может не быть. Адрес и размер LDT определяется в GDT, для обращения к LDT в процессоре существует специальный регистр (LDTR), но в отличии от GDTR он имеет размер 16 бит и содержит в себе селектор из GDT.

Дескрипторы сегментов.

Дескрипторные таблицы состоят из записей по 64 бита (8 байт) в каждой. Формат дескриптора таков:

7654
Базовый адрес 31-24Предел 19-16Права доступаБазовый адрес 23-16
3210
Базовый адрес 15-0Предел 15-0

Сразу бросается в глаза очень странная организация дескриптора, но это связано с совместимостью с процессором i286, формат дескриптора в котором был таков:

7654
ЗарезервированоПрава доступаБазовый адрес 23-16
3210
Базовый адрес 15-0Предел 15-0

Что же содержится в дескрипторе:

Базовый адрес - 32 бита (24 бита для i286). Определяет линейный адрес памяти, с которого начинается сегмент. В отличие от реального режима этот адрес может быть указан с точностью до байта.

Предел - 20 бит (16 бит для i286). Определяет размер сегмента (максимальный адрес, по которому может быть произведено обращение, это справедливо не всегда). 20-битное поле может показаться не очень то большим для 32-х битного процессора, но это не так. Оно не всегда показывает размер в байтах.

Байт прав доступа:

76543210
PDPLSTypeA

Бит P (present) - Указывает на присутствие сегмента в памяти. обращение к отсутствующему сегменту вызывает особый случай не присутствия сегмента в памяти.

Двух битное поле DPL определяет уровень привилегий сегмента. Про Уровни привилегий мы поговорим чуть позже.

Бит S (Segment)- Будучи установленным в 1, определяет сегмент памяти, к которому может быть получен доступ на чтение (запись) или выполнение.

Три бита Type - в зависимости от бита S определяет либо возможности чтения/записи, выполнения сегмента или определяет тип системных данных, хранимых в селекторе. Подробнее это выглядит так:
Если бит S установлен в 1, о поле Type делится на биты:

210
1 - кодПодчиненный сегмент кодаДопустимо считывание
0 - данныеРасширяется внизДопустима запись

Если сегмент расширяется вниз (это используется для стековых сегментов) то поле предела показывает адрес, выше которого допустима запись. ниже запись недопустима и вызовет нарушение пределов сегмента.
Бит А (Accessed) устанавливается в 1, если к сегменту производилось обращение.
Если бит S установлен в 0, то в сегменте находится служебная информация определяемая полем Typе и битом A.

TYPEAОписание
0001TSS для i286
0010LDT
0011Занятый TSS для i286
0100Шлюз вызова i286
0101Шлюз задачи
0110Шлюз прерывания i286
0111Шлюз исключения i286
1001TSS для i386
1011Занятый TSS i386
1100Шлюз вызова i386
1110Шлюз прерывания i386
1111Шлюз ловушки i386

Остальные комбинации либо недопустимы, либо зарезервированы.

TSS - это сегмент состояния задачи (Task state segment).

76543210
GD0UПредел 19-16

Шестой байт дескриптора, помимо старших бит предела, содержит в себе несколько битовых полей.
Бит G (Granularity) - определяет размер элементов, в которых измеряется предел. если 0 - предел в байтах, если 1 - размер в страницах.
Бит D (Default size) - размер операндов в сегменте. Если 0 - 16 бит. если 1 - 32 бита.
Бит U (User) - доступен для пользователя (вернее для программиста операционной системы)

Источник информации: http://www.uic.unn.ru/~kavs2/ZIP/AsmOS/OS/5.html

Пример программы

org 0x7c00

start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax

    lgdt [gdt_descriptor]

    mov eax, cr0
    or al, 1
    mov cr0, eax

    jmp 08h:init_pm

use32
init_pm:
    mov ax, 10h ; 00010 0 00 Сегмент №2 (gdt_data), кольцо защиты 0
    mov ds, ax
    mov es, ax

    ; Адрес видеопамяти для текстового режима
    mov edi, 0xB8000
    mov esi, msg1
    mov ah, 0x07 ; Атрибут: серый текст на черном фоне

.loop1:
    lodsb        ; Загрузить байт из [esi] в al
    test al, al  ; Проверка на конец строки (0)
    jz .done
    mov [edi], ax ; Записать символ и атрибут в видеопамять
    add edi, 2
    jmp .loop1

.done:
    jmp $

msg1 db 'HELLO', 0

; Таблица GDT
gdt_start:
    dq 0
gdt_code:
    dw 0FFFFh, 0, 9A00h, 00CFh
gdt_data:
    dw 0FFFFh, 0, 9200h, 00CFh
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

times 510-($-$$) db 0
dw 0xAA55

Разберем запись

gdt_code:
    dw 0FFFFh, 0, 9A00h, 00CFh
7654
Базовый адрес 31-24: 00hПредел 19-16: CFh
11001111
Права доступа: 9Ah
1 00 1 101 0
Базовый адрес 23-16: 00h
3210
Базовый адрес 15-0: 0Предел 15-0: 0FFFFh

Права доступа: 9Ah = 1 00 1 101 0

1

P (present)

сегмент находится в памяти

00

DPL

уровень привилегий сегмента

1

S (Segment)

может быть получен доступ на чтение (запись) или выполнение

101

Type

1 код, 0 - неподчинённый сегмент, 1 - допустимо считывание

0

А (Accessed)

к сегменту обращение не производилось

- -
- Разберем запись

gdt_data:
    dw 0FFFFh, 0, 9200h, 00CFh
7654
Базовый адрес 31-24: 00hПредел 19-16: CFh
11001111
Права доступа: 92h
1 00 1 001 0
Базовый адрес 23-16: 00h
3210
Базовый адрес 15-0: 0Предел 15-0: 0FFFFh

Права доступа: 9Ah = 1 00 1 101 0

1

P (present)

сегмент находится в памяти

00

DPL

уровень привилегий сегмента

1

S (Segment)

может быть получен доступ на чтение (запись) или выполнение

001

Type

0 - данные, 0 - не расширяется вниз, 1 - допустима запись

0

А (Accessed)

к сегменту обращение не производилось




Мы используем cookie-файлы для наилучшего представления нашего сайта. Продолжая использовать этот сайт, вы соглашаетесь с использованием cookie-файлов.
Принять