Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 OS
 osjournal 
 Protected Mode 
 Hardware 
 Kernels
  Dark Fiber
  BOS
  QNX
  OS Dev
  Lecture notes
  MINIX
  OS
  Solaris
  История UNIX
  История FreeBSD
  Сетунь
  Эльбрус
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...5172 
 Trees...943 
 Максвелл 3...874 
 Go Web ...826 
 William Gropp...811 
 Ethreal 3...790 
 Gary V.Vaughan-> Libtool...778 
 Ethreal 4...774 
 Rodriguez 6...769 
 Steve Pate 1...758 
 Ext4 FS...758 
 Clickhouse...757 
 Ethreal 1...745 
 Secure Programming for Li...734 
 C++ Patterns 3...721 
 Ulrich Drepper...701 
 Assembler...697 
 DevFS...668 
 Стивенс 9...655 
 MySQL & PosgreSQL...635 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

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 Mode16-bit Protected Mode32-bit Protected Mode
Базовый адрес сегмента20-битный (1M byte range) = 16 * segment register 24-битный (16M byte range), from descriptor32-битный (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 protectionnoyesyes
Segment registersegment base adr / 16selector selector

Казалось бы - protected mode не использует сегментированную память...

Сегмент в 32-bit protected mode может иметь размер до 4 гиг. Отсутствие ограничений создает иллюзию отсутствия сегментации. По этой причине этот режим так популярен.

Что такое дескриптор ?

В real mode размер сегмента равен 64K , и с сегментом можно делать все что угодно : хранить данные , стек , код и т.д. Базовый адрес регистра вычисляется как произведение 16 на значение одного из сегментных регистров .

В protected mode , окромя базового адреса сегмента , нам также необходим его размер и несколько флагов. Вся эта информация о сегменте организована в 8-байтной структуре данных , называемой descriptor:

Table 2: code/data segment descriptor.

Lowest byteByte 1Byte 2Byte 3Byte 4Byte 5Byte 6Highest byte
Limit 7:0Limit 15:8Base 7:0Base 15:8Base 23:16 AccessFlags, Limit 19:16Base 31:24

Это 32-битный дескриптор. Для 16-битных дескрипторов два старших байта обнулены (Limit 19:16, Flags, Base 31:24). Адресный байт указывает тип сегмента (data segment, stack segment, code segment, etc.):

Table 3: Адресный байт доступа для code/data дескрипторов.

Highest bitBits 6, 5Bit 4Bits 3Bit 2Bit 1Lowest bit
PresentPrivilege1ExecutableExpansion direction/ conformingWritable/ readableAccessed
  • 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 bitBit 6Bit 5Bit 4
GranularityDefault Size00

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 byteByte 1Byte 2Byte 3Byte 4Byte 5Byte 6Highest byte
Offset 7:0Offset 15:8Selector 7:0Selector 15:8Word Count 4:0AccessOffset 23:16Offset 31:24

Table 6: Байт доступа для системных дескрипторов.

Highest bitBits 6, 5Bit 4Bits 3, 2, 1, 0
PresentPrivilege0Type

Table 7: Типы системных сегментов.

TypeSegment functionTypeSegment function
0(invalid)8(invalid)
1Available '286 TSS9Available '386 TSS
2LDT10(undefined, reserved)
3Busy '286 TSS11Busy '386 TSS
4'286 Call Gate12'386 Call Gate
5Task Gate13(undefined, reserved)
6'286 Interrupt Gate14'386 Interrupt Gate
7'286 Trap Gate15'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); }

    Пробелы в мессагах интерпретируются как атрибутивные байты , отчего текст выводится на зеленом фоне.

    Оставьте свой комментарий !

    Ваше имя:
    Комментарий:
    Оба поля являются обязательными

     Автор  Комментарий к данной статье