Protected Mode
By Chris Giese
Что такое защищенный режим?
8088 CPU был не масштабируемым.
Он имел доступ всего к 1 метру памяти.
Интел разработал 80286 CPU , который имел 2 режима:
real mode -
protected mode - (16-bit protected mode).
Последний позволял иметь доступ более чем к 1 метру памяти , а также имел механизмы защиты.
32-bit protected mode появился уже в '386 CPU.
Чем различаются Real Mode и Protected Mode ?
Table 1: разница между real- и protected modes.
| Real Mode | 16-bit Protected Mode | 32-bit Protected Mode |
Базовый адрес сегмента | 20-битный (1M byte range) = 16 * segment register |
24-битный (16M byte range), from descriptor | 32-битный (4G byte
range), from descriptor |
Ограничение на размер сегмента | 16-bit, 64K bytes (fixed) | 16-bit, 1-64K bytes |
20-bit, 1-1M bytes or 4K-4G bytes |
Segment protection | no | yes | yes |
Segment register | segment base adr / 16 | selector |
selector |
Казалось бы - protected mode не использует сегментированную память...
Сегмент в 32-bit protected mode может иметь размер до 4 гиг.
Отсутствие ограничений создает иллюзию отсутствия сегментации.
По этой причине этот режим так популярен.
Что такое дескриптор ?
В real mode размер сегмента равен 64K , и с сегментом можно делать все что угодно :
хранить данные , стек , код и т.д. Базовый адрес регистра вычисляется как произведение
16 на значение одного из сегментных регистров
.
В protected mode , окромя базового адреса сегмента ,
нам также необходим его размер и несколько флагов.
Вся эта информация о сегменте организована в 8-байтной структуре данных ,
называемой descriptor:
Table 2: code/data segment descriptor.
Lowest byte | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Highest byte |
Limit 7:0 | Limit 15:8 | Base 7:0 | Base 15:8 | Base 23:16 |
Access | Flags, Limit 19:16 | Base 31:24 |
Это 32-битный дескриптор. Для 16-битных дескрипторов два старших байта обнулены
(Limit 19:16, Flags, Base 31:24).
Адресный байт указывает тип сегмента (data segment, stack segment, code segment, etc.):
Table 3: Адресный байт доступа для code/data дескрипторов.
Highest bit | Bits 6, 5 | Bit 4 | Bits 3 | Bit 2 | Bit 1 | Lowest bit |
Present | Privilege | 1 | Executable | Expansion direction/ conforming | Writable/ readable | Accessed |
- Present bit. Должен быть установлен в 1.
- Privilege. 0 - высший уровень привилегий (Ring 0), 3 - низший (Ring 3).
- Executable bit. 1 - кодовый сегмент , иначе - stack/data segment.
- Expansion direction (stack/data segment). Если 1 - сегмент растет вниз.
- Conforming (code segment). Privilege-related.
- Writable (stack/data segment). Если 1 - сегмент перезаписываемый.
- Readable (code segment). Если 1 - сегмент на чтение
- Accessed. Бит устанавливается независимо от того , на запись или на чтение.
The 4-битный флаг:
Table 4: flags .
Highest bit | Bit 6 | Bit 5 | Bit 4 |
Granularity | Default Size | 0 | 0 |
Granularity bit - равен 1 , если ограничение на страницу памяти = 4 килобайтам.
Default Size - B (Big) bit, определяет размерность операндов машинных команд - 16- или 32-bit .
Для кодовых сегментов бит D определяет размерность операндов.
Например , чтобы установить бит D в 1 , надо использовать что-то типа USE32,
Следующая последовательность 16-ричных кодов : B8 90 90 90 90
будет интерпретирована какmov eax, 90909090h
В случае с командой (USE16) эти байты станут командами
mov ax,9090h
nop
nop
Бит 4 Access byte - если он равен нулю, мы имеем system segment.
- Task State Segment (TSS). Используется для многозадачности.
'386 и выше имеют 4 разновидности TSS.
- Local Descriptor Table (LDT). Тут задачи хранят свои собственные дескрипторы.
- Gates. Гейты контролируют процессор на предмет перехода с одного уровня привилегий на другой.
Они имеют отличную от дескрипторов структуру :
Table 5: gate descriptor.
Lowest byte | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Highest byte |
Offset 7:0 | Offset 15:8 | Selector 7:0 | Selector 15:8 | Word Count 4:0 | Access | Offset 23:16 | Offset 31:24 |
Table 6: Байт доступа для системных дескрипторов.
Highest bit | Bits 6, 5 | Bit 4 | Bits 3, 2, 1, 0 |
Present | Privilege | 0 | Type |
Table 7: Типы системных сегментов.
Type | Segment function | | Type | Segment function |
0 | (invalid) | | 8 | (invalid) |
1 | Available '286 TSS | | 9 | Available '386 TSS |
2 | LDT | | 10 | (undefined, reserved) |
3 | Busy '286 TSS | | 11 | Busy '386 TSS |
4 | '286 Call Gate | | 12 | '386 Call Gate |
5 | Task Gate | | 13 | (undefined, reserved) |
6 | '286 Interrupt Gate | | 14 | '386 Interrupt Gate |
7 | '286 Trap Gate | | 15 | '386 Trap Gate |
Запомним , что TSS, LDT, и гейты - 3 главных типа системных сегментов.
Где живут дескрипторы ?
Они хранятся в памяти в специальных таблицах : Global Descriptor Table (GDT), Interrupt Descriptor Table (IDT),
и Local Descriptor Tables. В CPU есть 3 специальных регистра по такому случаю :
GDTR - указатель на GDT, IDTR - указатель на IDT (в случае , если используются прерывания),
и LDTR - указатель на LDT (если LDT используется).
Каждая из этих таблиц может хранить до 8192 дескрипторов.
Что такое селектор ?
Селектор - это индекс дескрипторной таблицы.Под селектор отводится 13 старших бит.
Следующий бит указывают на GDT или LDT.
Младшие 2 бита устанавливают уровень привилегий.
Как перейти в protected mode?
Для этого нужно:
- Создать Global Descriptor Table (GDT),
- (optional) создать Interrupt Descriptor Table (IDT),
- запретить прерывания,
- загрузить в GDTR указатель на GDT,
- (optional) загрузить в IDTR указатель на IDT,
- установить бит PE в регистре MSW,
- сделать т.н. far jump (загрузка CS плюс IP/EIP) (в CS загрузить адрес кодового сегмента),
- загрузить регистры DS и SS соответственно data/stack segment selector,
- проинитить pmode stack,
- (optional) разрешаем interrupts.
Как вернуться назад в Real Mode?
Для '386:
- запретить прерывания,
- делаем far jump в 16-битный кодовый сегмент ,
- грузим SS 16-битным селектором data/stack segment,
- обнуляем PE bit,
- делаем far jump в real-mode адрес,
- грузим DS, ES, FS, GS, SS real-mode-значениями,
- (optional) устанавливаем IDTR real-mode значение (base 0, limit 0xFFFF),
- включаем прерывания.
Перед выполнением кода в real mode, CS и SS нужно загрузить селекторами,
которые указывают на дескрипторы , которые поддерживают "real mode".
Здесь есть ограничение на 64K bytes, byte-granular (Flags nybble=0),
writable (data/stack segment only), Access byte=1xx1001x.
Где можно попасть в засаду ?
- Мельчайшим деталям нужно уделять самое пристальное внимание.
Один неверный бит может погубить все.
Ошибки Protected mode запросто перегружают CPU.
- Стандартные библиотеки не работают. Стандартный printf() не будет работать , поскольку использует
прерывание BIOS .
- Перед очисткой PE bit, сегментные регистры должны указывать на дескрипторы ,
которые соответствуют real mode.
Для DS, ES, FS , GS, ограничение на сегмент должно быть 0xFFFF или больше.
Если вы ограничиваете сегмент 0xFFFFF и делаете его page-granular,
вы можете получить доступ к 4G памяти из real mode. Это называется unreal mode.
Ограничение , отличное от 0xFFFF (или page-granularity) для CS или SS вызывает большие проблемы в real mode.
- Можно не использовать для '286 команду LMSW для очистки PE bit. Используйте MOV CR0, nnn.
- Загружайте все сегментные регистры селекторами после входа в protected mode.
- IDTR должен надо обнулять перед разрешением прерываний.
-
Не все инструкции работают в real mode.
Например , не работает LTR.
В 2-х словах :
- Чтобы вернуться в real mode - просто нажми reset button :)
- Не отключайте прерывания.
- Не используйте LDT.
- В GDT достаточно положить 4 дескриптора: null, code, stack/data, и text video.
- Для установки базового адреса сегмента в real-mode умножьте 16 на значение сегментного регистра.
- Для сегментов установите ограничение по максимуму (0xFFFF дляr 16-bit protected mode).
- Избегайте нулевых привилегий (Ring 0, highest privilege).
- Установите exception handlers , которые будут выводить сообщения на экран:
void unhand(void)
{ static const char Msg[]="U n h a n d l e d I n t e r r u p t ";
disable();
movedata(SYS_DATA_SEL, (unsigned)Msg,
LINEAR_SEL, 0xB8000,
sizeof(Msg));
while(1); }
Пробелы в мессагах интерпретируются как атрибутивные байты ,
отчего текст выводится на зеленом фоне.
|