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...5164 
 Trees...935 
 Максвелл 3...860 
 Go Web ...814 
 William Gropp...794 
 Ethreal 3...778 
 Ethreal 4...766 
 Gary V.Vaughan-> Libtool...764 
 Rodriguez 6...755 
 Ext4 FS...748 
 Clickhouse...748 
 Steve Pate 1...747 
 Ethreal 1...736 
 Secure Programming for Li...718 
 C++ Patterns 3...711 
 Ulrich Drepper...691 
 Assembler...686 
 DevFS...653 
 Стивенс 9...643 
 MySQL & PosgreSQL...621 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

Protected Mode Tutorial

by : Peter Quiring
date : Aug/97

Содержание
Overview
Why Protected mode?
The 80386 Processor
Selectors and descriptors
Descriptors
Exception Handlers
Special 386 Registers
V86 Mode
The DPMI Standard
Calling RMODE from PMODE
Transfering control from RMODE to PMODE
Default Segment Size
16bit PMODE vs. 32bit PMODE
Last note and "QLIB"

Overview
Цель этого руководства - показать разницу между protected mode и real mode,и как начать программировать DOS-расширители (примером DOS-extenders являются DOS/4GW или PMODE/W).
Real mode - родной 8086 режим с 16-bit регистрами и ограничением памяти в 640k.
Защищенные режимы предлагают больше.
Это руководство к созданию программ , которые будут работать под дос-расширением,а не о том ,как создать сам дос-расширитель.
Сокращения:
RMODE = real mode
PMODE = protected mode
V86 = virtual 8086 mode
Почему именно Protected Mode?
Вы получаете доступ к бОльшим ресурсам. Барьер в 640k становится преодолимым.
Protected mode позволяет сделать более защищенные программы , позволяет реализовать операционную систему.
Функция DOS extenders заключается в загрузке ваших программ в защищенный режим.
386 Processor
32-битный PMODE появился вместе с 80386 processor. 80286 был первым CPU , имевшим PMODE , но это был 16-битный PMODE. 80286 мог поддерживать 16 MB RAM , которая выросла уже до 4GB на 386. В 32-bit PMODE у вас есть доступ ко всей памяти напрямую.
В 80386 CPU в основном все регистры 32-битные.
порядок бит :
 31      16 15 8 7  0
 +---------+----+----+
 |         | AH | AL |
 +---------+----+----+
           [-- AX  --]
 [------- EAX -------]
 
 +---------+----+----+
 |         | BH | BL |
 +---------+----+----+
           [-- BX  --]
 [------- EBX -------]
 
 +---------+----+----+
 |         | CH | CL |
 +---------+----+----+
           [-- CX  --]
 [------- ECX -------]
 
 +---------+----+----+
 |         | DH | DL |
 +---------+----+----+
           [-- DX  --]
 [------- EDX -------]
 
 +---------+----+----+
 |         |   SI    |
 +---------+----+----+
 [------- ESI -------]
 
 +---------+----+----+
 |         |   DI    |
 +---------+----+----+
 [------- ESI -------]
 
 +---------+----+----+
 |         |   BP    |
 +---------+----+----+
 [------- EBP -------]
 
 +---------+----+----+
 |         |   SP    |
 +---------+----+----+
 [------- ESP -------]
 
 +---------+----+----+
 |         |  flags  |
 +---------+----+----+
 [----- Eflags ------]
 
Сегментные регистры имеют размерность в 16 бит.
32-битные регистры позволяют получить доступ к 4GBs памяти. 80386 имеет новый адресный режим , называемый scaled index base mode (SIB).
Можно разместить 2 любых регистра внутри квадратных скобок и один из них умножить на 1,2,4 или 8. Можно прибавить смещение.
Пример:
 mov al,[eax*2]
 mov bl,[ebx+ecx*4+my_offset]
 mov [esp+eax*2],edi
 
Несколько новых инструкций :
  • iretd - 32bit iret
  • pusad/pushfd/popad/popfd - 32bit versions
В ассемблерный файл нужно добавлять директиву ".386" , чтобы использовать эти инструкции.
Селекторы и дескрипторы
Когда 386 находится в real mode , он ведет себя как 8086. При обращении к памяти каждый раз значение из сегмента умножается на 10h и прибавляется смещение - так получается физический адрес.
В PMODE все меняется. Значение в сегментном регистре теперь тоже смещение внутри системных таблиц и называется selector , и к этому смещению прикручивваются дополнительные атрибуты - descriptors.
В дескрипторах хранится информация о правах , дескриптор указывает на сегмент , в котором может храниться код , данные либо прерывания.
Теперь в сегментных регистрах не найдешь значений типа 0a000h. Поскольку регистры теперь 32-битные , можно использовать память в диапазоне 4 GB.
Пример загрузки видеопамяти :
mov edi,0b8000h    ;Colour text video segment
 mov [edi],al       ;put a byte on the text screen
 mov edi,0400h      ;BIOS segment
 mov eax,[edi+67h]  ;get the system timer tick (IRQ#1)
 

Системные таблицы GDT, LDT, IDT
3 основных типа:
GDT - Global Descriptor Table
LDT - Local Descriptor Table
IDT - Interrupt Descriptor Table

Таблицу GDT использует каждая работающая программа. Она может хранить 8,192 дескрипторов, первый всегда зарезервирован.
LDT выполняет аналогичную функцию что и GDT но для локальной задачи.
IDT хранит обработчики прерываний. Она может хранить только 256 дескрипторов. RMODE interrupt table располагается в самом начале памяти, PMODE interrupt table может быть где угодно.
Селектор может выглядеть вот так : :
Selector layout
      +---------------------------+
 bit  | 15 ...     3 | 2  | 1   0 |
 desc | Descriptor   | TI |  PL   |
      +---------------------------+
 
Биты 15-3 - это индекс дескриптора. Это 13 бит : 2^13 = 8192.
Бит 2 называется TI (table indicator). Если он = 0 , селектор указывает на GDT, иначе (1) он указывает на LDT.
Биты 1 и 0 - уровень привилегий. Они обеспечивают механизм защиты CPU.
CPU имеет специальные регистры для загрузки таких таблиц: gdtr - GDT Register
idtr - IDT Register
LDT не имеет такого регистра.
Это 48-битные регистры. 32 бита задают адрес памяти и остальные 16 бит - размер сегмента.
Ограничение = чмсло байт - 1.
Дескрипторы
Существуют 18 различных типов дескрипторов. Многие из них зарезервированы или устарели.
Каждый дескриптор имеет следующую информацию:
BASE : Стартовый адрес сегмента. Например 0a0000h для видео-памяти. Затем
LIMIT : это размер сегмента.
TYPE : тип сегмента - их 3 - код,данные(или стек) и гейты. Гейт - это обработчик прерывания. Дескриптор задает тип сегмента на чтение-запись Например , стек - сегмент на запись.
Исключения
Исключение (или fault) генерится CPU при возникновении ошибки. Исключения имеют нумерацию от INT #0h до INT #01fh.
Например #13h. это исключение генерится в следующем случае:
  • Загрузка CS (с помощью JMP или CALL) дескриптором , который ссылается не на кодовый сегмент.
  • запись в кодовый сегмент
  • загрузка SS дескриптором,который указывает не на записываемый сегмент
  • попытка переключиться в PMODE напрямую из V86 mode
И т.д.
  • INT # 0h - деление на ноль
  • INT # 1h - Debug trap (для 386)
  • INT # 3h - User Breakpoint (debugging)
  • INT # 13h - exc Handler (перехватывает все исключения ,которые еще ничем не перехвачены - catch-all handler).
Специальные регистры (CRx)
В 386 появились специальные регистры для системной конфигурации, дебага,тестирования.
Вот некоторые из них:
CR0 - его нулевой бит указывает на то , в каком режиме находится CPU - PMODE(1) или RMODE(0).
DR0 -> DR7 - debugging registers.
TR0 -> TR? - тестирование.
Virtual 8086 Mode (V86)
386 имеет специальный режим , названный V86 mode. Это симбиоз PMODE + RMODE.
Этот режим используется тогда , когда бит 17 регистра EFLAGS установлен в 1.
Программы RMODE могут выполняться при этом в режимеr PMODE.
DPMI Standard
В начале PMODE CPU не совсем корректно обрабатывал исключения. Приложения RMODE могли напрямую установить бит 0 регистра CR0 для попадания в PMODE. Если другое приложение при этом пыталось выполнить доступ к памяти, вызывалась ошибка.
Был разработан VCPI для того чтобы в режие V86 позволялось многим приложениям иметь корректный доступ к памяти.
Но со временем VCPI перестали использовать в многозадачных ОС , поскольку с его помощью приложениям давалась слишком большая степень свободы, вот тут-то и родился DPMI server.
DPMI (Dos Protected Mode Interface) позволяет многим приложениям работвть в PMODE , сохраняя при этом системную безопасность.
DPMI также реализует многие сервисы по выделению-освобождению ресурсов памяти.
DPMI server работает во много аналогично DOS INT 21h прерыванию. Для вызова DPMI server нужно использовать INT 31h только в режиме PMODE. Нельзя использовать DPMI server из RMODE.
Вызов RMODE из PMODE
Находясь в PMODE , при необходимости можно вернуться в RMODE для вызова специфичных RMODE INT или других RMODE-функций.
Если нужно вызвать RMODE INT без защищенного параметра, можно просто вызвать INT прямо из PMODE. Если вы попытаетесь загрузить сегментный регистр в PMODE , а это значение вдруг не имеет прав на загрузку, программа может потерпеть крах.
DOS extender выполняет сам специалные extend расширения для некоторых INT-вызовов. К ним относятся большинство INT 21h .
DPMI server обеспечивает вызов RMODE INT с помощью специальной структуры:
callstruct struct   ;50 bytes  (32h)
   _edi dd ?     ;0
   _esi dd ?     ;4
   _ebp dd ?     ;8
   _res dd ?     ;0ch reserved
   _ebx dd ?     ;10h
   _edx dd ?     ;14h
   _ecx dd ?     ;18h
   _eax dd ?     ;1ch
   _flg dw ?     ;20h flags
   _es dw ?      ;22h segments (NOT selectors)
   _ds dw ?      ;24h  "
   _fs dw ?      ;26h  "
   _gs dw ?      ;28h  "
   _ip dw ?      ;2ah ignored in some calls
   _cs dw ?      ;2ch  "
   _sp dw ?      ;2eh must be 0 to use system stacks
   _ss dw ?      ;30h  "
 callstruct ends
 
Эту структуру нужно заполнить для передачи в RMODE INT handler или функцию. Нужно передавать сегментные значения , не селекторы.
Поля _sp и _ss надо обнулить для вызова системного стека.
Если вы делаете CALL для RMODE-функции, _cs и _ip должны быть загружены FAR-адресами.
Есть DPMI-функции для реализации RMODE calling:
ax = 300h - Simulate RMODE INT
   bl = INT #
   bh = 0  (must always be zero)
   cx = ?  (number of dwords to pass on stack)
   es:edi = offset of 'callstruct'
 ax = 301h - Call RMODE Procedure with FAR stack frame
   bh = 0  (must always be zero)
   cx = ?  (number of dwords to pass on stack)
   es:edi = offset of 'callstruct'
 ax = 302h - Call RMODE Procedure with IRET stack frame
   bh = 0  (must always be zero)
   cx = ?  (number of dwords to pass on stack)
   es:edi = offset of 'callstruct'
 
Регистр cx указывает на то ,сколько параметров скопировано из PMODE=стека в RMODE-стек. Размер каждого параметра должен быть dwords в 32bit PMODE программе или 16bit в PMODE программе.
Вызов ISR (interrupt service rountine). Нужно создать структуру в стеке перед вызовом :
myISR proc
   pushad
   sub esp,sizeof callstruct     ;create a callstruct buffer
   mov edi,esp
   ...   ;place your code here
   add esp,sizeof callstruct     ;remove callstruct buffer
   popad
   iretd
 myISR endp
 
Внутри процедуры регистр EDI хранит буфер для этой структуры , создаваемой каждый раз перед вызовом ISR.
Передача контроля между RMODE и PMODE
Для этого нужно выделять память для RMODE callback из DPMI host. Каждый раз необходимо создавать структуру,которая будет хранить RMODE-регистры при переходе в PMODE. При возвращении оттуда вы получаете значение в CS:IP от DPMI host .
ES:EDI = callstruct you provided
 DS:ESI = top of RMODE stack (SS:SP)
 interrupts disabled
 
При возврате ( IRETD) :
ES:EDI = callstruct to be used for returning to RMODE
 
Пример:
simulate_retf proc
   ;Assume ES:EDI -> callstruct
   ;       DS:ESI -> RMODE stack
   xor eax,eax
   mov ax,es:[edi].callstruct._ss
   shl eax,4
   xor ebx,ebx
   add bx,es:[edi].callstruct._sp
   add eax,ebx
   mov bx,[eax]
   mov es:[edi].callstruct._ip,bx
   mov bx,[eax+2]
   mov es:[edi].callstruct._cs,bx
   add es:[edi].callstruct._sp,4
   iretd
 simulate_retf endp
 
 simulate_iret proc
   ;Assume ES:EDI -> callstruct
   ;       DS:ESI -> RMODE stack
   xor eax,eax
   mov ax,es:[edi].callstruct._ss
   shl eax,4
   xor ebx,ebx
   add bx,es:[edi].callstruct._sp
   add eax,ebx
   mov bx,[eax]
   mov es:[edi].callstruct._ip,bx
   mov bx,[eax+2]
   mov es:[edi].callstruct._cs,bx
   mov bx,[eax+4]
   mov es:[edi].callstruct._flg,bx
   add es:[edi].callstruct._sp,6
   iretd
 simulate_iret endp
 
Размер сегмента
В дескрипторе есть флаг,который указывает на природу сегмента - 16bit или 32bits. Для 16bit режима (RMODE или PMODE) :
  0100:0000         89h d8h          mov ax,bx
 
Для 32bit :
  0100:0000         89h d8h          mov eax,ebx
 
Если вы захотите использовать 32-битный регистр для 16-битного сегмента, нужно использовать специальный префикс.Этот префикс - 066h.
  db 66h
   retf
 

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

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

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