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...809 
 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 Marius Marcu

Memory Managment

Ключ к пониманию Protected Mode лежит в адресации памяти. Почти все расширенные возможности процессора основаны на этом. Многозадачность , привилегии , доступ к 4 гигам памяти происходят именно отсюда.

Для 286 с его 24 адресными линиями и 16-битными регистрами , доступ к 4 GB невозможен. Эти возможности появились в 386.

Виртуальная адресация

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

По необходимости , операционная система может поместить код или данные на диск. Виртуальный адрес - 16-битный селектор плюс 32-битное смещение. Сначала селектор должен быть загружен в один из сегментных регистров - CS, DS, ES, FS или GS, а затем уже грузится смещение внутри этого сегмента.

Также нужно понимать тот факт , что селектор - это не реальный адрес памяти. Это индекс сегментного дескриптора в специальной таблице , или указатель на реальный адрес . А уже этот адрес, помимо реального физического содержания, содержит в себе дополнительные битовые атрибуты , характеризующие данный сегмент, размер сегмента. И уже окончательный адрес памяти строится как комбинация селектора плюс смещения

Дескрипторы

Дескриптор - это специальная структура.Его размер = 8 байт. Его атрибуты :
     * реальный физический стартовый адрес сегмента (32 Bit)
     * длина сегмента (20 Bit)
     * дополнительные биты : права , тип и т.д.
 
Следующая картинка показывает дескриптор 80386 :

Дополнительная битовая информация о сегменте хранится в 6 и 7 байтах. 6- байт :

Поле TYPE определяет тип сегмента и набор разрешенных операций:

Следующая таблица дает набор возможных комбинаций типов сегментов :

Номер Значение TYPE Тип сегмента
0 000b Datasegment (read only)
1 001b Datasegment
2 010b reserved
3 011b "expand down" Datasegment
4 100b Codesegment (not readable, execute only)
5 101b Codesegment
6 110b "Conforming"-Codesegment (not readable, execute only)
7 111b "Conforming"-Codesegment

Дополнительная информация хранится в 7-м байте :

Пример дескриптора

  Создадим дескриптор со следующими характеристиками :
     * стартовый физический адрес сегмента -  01F2E3Dh
     * длина сегмента - 2 метра (2097152d=200000h)
     * это сегмент данных (читаемый и записываемый)
     * DPL= 2
     * тип сегмента - 80386-segment
 
Напишем несколько строк асм-кода :
  my_descriptor:  dw 0200h            ; размер сегмента (bit 0..15)
                  dw 2E3Dh            ; базовый адрес сегмента (bit 0..15)
                  db 1Fh              ; базовый адрес сегмента (bit 16..23)
                  db 11010010b        ; доступ и тип 
                  db 11000000b        ; дополнительная информация и размер (bit 16.19) 
                  db 0                ; базовый адрес сегмента (bit 24..31) 
 
Первое слово в дескрипторе - размер сегмента. Он у нас 2 метра . Поскольку размер сегмента больше метра , устанавливаем бит granularity в единицу - т.е. размер страницы памяти равен теперь 4 KB (это в 7-м байте).

Поскольку размер в 2 MB характеризует т.н. большой сегмент , нужно выполнить 3 условия :

         
   1. Тип сегмента - 80386 (контролируется битом B в байте 7).
   2. Бит Granularity нужно установить в 1 для постраничной организации 
      размером 4096 byte (G-бит в байте 7).
   3  Длину сегмента нужно сконвертировать в формат 4 KB unit 
   	(т.е. разделить размер сегмента на 4096).
         2 MB= 2097152d / 4096d=512d= 200h.
 
Второе слово - это биты с 16 по 32 - содержат базовый адрес сегмента (в данном случае младшее слово в адресе 001F 2E3Dh, т.е. 2E3Dh)

Третье слово состоит из 2-х байтов. нижний байт включает биты 16..23 базового адреса сегмента (в нашем примере: 1Fh).Старший байт включает параметры доступа и типа :

 	 
 	 Биты доступа / типа : 1 10 1 001 0:
 	o первый бит слева - сегмент доступен
 	o следующие 2 бита - уровень привилегии  (10 = уровень 2).
 	o следующий бит=1 - это тип - сегмент
 	o следующие 3 бита - поле TYPE - сегмент типа 1 (data segment, reading + writing)
 	o последний бит - ACCESS = 0 
 
4-е слово состоит из 2-х байтов. Старший , 8-й байт , включает биты 24..31 базового сегментного адреса, в нашем случае (001F 2E3Dh). 7-й байт включает : 16..19 - биты длины сегмента.

Дескрипторные таблицы

Дескрипторы сводятся в линейную таблицу. В такой таблице может быть не более 8192 дескрипторов ( 64 KB (65536) / 8 = 8192)

"Global Descriptor Table" - GDT .
"Interrupt Descriptor Table" - IDT - включает специальные дескрипторы - гейты - Gates. Гейт используется для хранения адреса процедуры прерывания. Операционная система обязана обрабатывать любое потенциальное исключение. Также дескрипторную таблицу можно создать для любой задачи - это будет LDT.

В следующем примере показаны 3 дескриптора таблицы GDT :

      descriptor_0 db 8 dup (?)          
      descriptor_1 db 8 dup (?)          
      descriptor_2 db 8 dup (?)
  
Адрес Самой GDT надо загрузить в регистр GDTR :
     GDTSTRUC STRUC                   
 	 Limit    dw ?                   
 	 BaseAddr dd ?          
     GDTSTRUC ENDS           
 	 
 	 gdt_adr  GDTSTRUC ?  
 	
 	; определим размер Global Descriptor Table 	 		      
 	; размер GDT = 3*8 42 байта
          mov [ gdt_adr.Limit ], 3*8  
 	
 	; установим базовый адрес
 	; преобразование логического адресатипа segment:offset
 	; в 32-битный линейный адрес
 	xor eax,eax           
 	mov ax,seg descriptor_0           
 	shl eax,4           
 	add eax,offset descriptor_0           
 	mov [ gdt_adr.BaseAdr ],eax  
 	; загружаем регистр GDTR 
 	lgdt gdt_adr
  

Селектор

Виртуальный адрес состоит из 2-х частей - селектора и смещения. Смещение мы только что рассмотрели. Селектор имеет бит TI ("Table Indicator") , который указывает либо на GDT , либо на LDT. Также имеется бит RPL ("Requested Privilege Level") - бит уровня привилегий :

Рассмотрим селектор :

 	Selector: 02FBh (binary: 0000001011111011b)            
 	TI bit =  0  ==> таблица GDT           
 	RPL    = 11b ==> уровень привилегий (3)           
 	Index  = 0000001011111b = 05Fh
 
Для селектора есть особенность - он не может быть загружен напрямую в селектор командой MOV. А обойти это можно так :
 	MOV AX,02FBh  
 	MOV DS,AX
 

Инициализация защищенного режима

Интеловские процессоры при загрузке начинают работать в Realmode . Protected Mode нужно активировать. Для этого нужно установить бит PE регистра CR0. Это нельзя сделать напрямую с помощью команды MOV. Это можно сделать так :
 	mov eax,cr0
 	or  eax,1       ;  PE=1
 	mov cr0,eax	
 
Перед установкой бита PE нужно проинициализировать GDT . Также необходимо проинициализировать IDT , которая будет обрабатывать исключения и прерывания. Для непосредственного перехода из real mode в protected mode нужно сделать т.н. FAR-JMP (например JMP 02FB:0000).

IDT

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

Для того чтобы пользовательский процесс общался с операционной системой , существуют гейты. Существуют 4 основных типа гейтов :

Gate Function
Call Вызов системной подпрограммы
Interrupt прерывания как от железа , так и от софта (INT)
Trap также 2 типа : hardware Interrupts и INT-инструкции
Task команды: CALL, JMP, INT и hardware Interrupts)

Гейты trap и interrupt отличаются только битом IF ("Interrupt Enable Flag") флагового регистра. Гейт interrupt сначала обнуляет этот бит , потом восстанавливает его после команды IRET. Гейт trap его вообще не трогает.

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

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

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