Процессор Intel в защищенном режиме #9

Дата публикации 12 май 2003

Процессор Intel в защищенном режиме #9 — Архив WASM.RU

ПРОЦЕССОР INTEL В ЗАЩИЩЕННОМ РЕЖИМЕ Выпуск №9

После длительного затишья мы продолжаем изучать защищенный режим. В предыдущем выпуске был рассмотрен пример переключения процессора в защищенный режим БЕЗ использования страничной адресации. В данном выпуске будет рассмотрен код перевода проца в защищенный режим С использованием страничной адресации. По сути - код остался прежним, я лишь опишу изменения, которые необходимо внести.

Приступим. Следует иметь ввиду, что все изменения для включения страничной адресации производятся уже ПОСЛЕ ПЕРЕХОДА В ЗАЩИЩЕННЫЙ РЕЖИМ !!! И еще - страницы у нас будут 4-килобайтными!!!

И вот они, эти изменения:

  1. Подготовить каталог страниц
  2. Заполнить таблицу страниц
  3. Адрес каталога страниц поместить в регистр CR3
  4. Включить страничную адресацию (установка бита 31 в регистре CR0)

THAT'S ALL!

Видишь, как все просто? А ты боялся!

Вот картинка (она уже у нас фигурировала), из которой видно для чего нужны эти 4 пункта:

Прежде, чем приступить, хочу вкратце рассказать, чего же мы будем творить. Вопрос в том, КАК НАГЛЯДНО ПОКАЗАТЬ РАЗНИЦУ МЕЖДУ СЕГМЕНТНОЙ И СЕГМЕНТНО-СТРАНИЧНОЙ адресацией? Оказывается, это не так-то уж и просто... Но вот как я попытаюсь этого добиться:

В предыдущем выпуске рассылки мы рассмотрели код, который выводит строку на экран, т.е. все свелось в конце концов к тому, что по адресу ES:0 вывелась надпись, где ES - селектор на предварительно подготовленный дескриптор, база которого равна 0B8000h (т.е. начало видеопамяти в текстовом режиме). Все однозначно и понятно. Теперь, как ты уже знаешь, при страничной адресации адрес 0B8000h препарируется на три части: старшие 10 бит - номер элемента в каталоге страниц, средние 10 бит - номер элемента в таблице страниц, и, наконец, младшие 12 бит - это СМЕЩЕНИЕ В СТРАНИЦЕ.

Недолго думая, можно умозаключить, что, попытавшись что либо записать по адресу 0B8000h при СТРАНИЧНОЙ адресации, мы не обнаружим там никакого начала видеопамяти, разумеется, если ПРЕДВАРИТЕЛЬНО (!) мы корректно не настроим каталог и таблицы страниц. Вот она, виртуальность! Вроде, вот же он: адрес начала видеопамяти! 0B8000h! У нас перед носом! Ан, нет... Это всего лишь иллюзия, т.к. этот адрес - не линейный, а виртуальный. Вот разбить его на три части, пройтись по каталогу и таблице страниц - вот тогда сформируется ОКОНЧАТЕЛЬНЫЙ АДРЕС (ВЫСТАВЛЯЕМЫЙ ПРОЦЕМ НА ШИНУ), который (при страницах в 4Кб) В ДАННОМ, КОНКРЕТНОМ СЛУЧАЕ (0B8000h) может принять значение от 0 до 1Мб!!! Надеюсь, понятно почему именно эти значения?

Поэтому вот оно, золотое правило: ПРИ СТРАНИЧНОЙ АДРЕСАЦИИ ЛИНЕЙНЫЙ АДРЕС ФОРМИРУЮТ ЭЛЕМЕНТЫ КАТАЛОГОВ И ТАБЛИЦ СТРАНИЦ, ну и конечно "проводником" по этим таблицам служит сам ВИРТУАЛЬНЫЙ адрес...

Значит, это все к чему? А к тому, что если представить все это дело НАОБОРОТ, то придем точно к такому же выводу! Т.е. я хочу сказать, что если 0B8000h - это на самом деле нечто совершенно иное, то с точно таким же успехом, ЛЮБОЙ ВИРТУАЛЬНЫЙ АДРЕС ПОСЛЕ ПРЕОБРАЗОВАНИЙ МОЖЕТ ПРЕВРАТИТЬСЯ В 0B8000!!! НАМ СТОИТ ТОЛЬКО ЗАХОТЕТЬ ЭТОГО! А что значит захотеть? А это значит, соответствующим образом заполнить элементы каталога и таблицы страниц, только и всего... На примере все станет совершенно ясно.

Вот что мы еще сделаем: вообще уберем дескриптор сегмента видеопамяти из таблицы GDT (итого у нас останется два дескриптора - кода и данных). Для чего - станет ясно ниже.

Итак, где-нибудь сразу после перехода в защ. режим и загрузки сегментных регистров соотв. селекторами нужно создать КАТАЛОГ СТРАНИЦ.

Код (Text):
  1.  
  2. ENTRY_POINT:
  3. ; загрузим сегментные регистры селекторами на соответствующие дескрипторы:
  4.           mov    AX,00010000b       ; селектор на второй дескриптор (DATA_descr)
  5.         mov  DS,AX          ; в DS его
  6.         mov  ES,AX              ; и в ES его же

вот теперь создадим каталог страниц. Что это такое? Каталог страниц - это набор 32- разрядных записей (элементов); вообще, структура записей подробно представлена в седьмом выпуске рассылки, поэтому кто забыл - посмотрите.

Начало каталога страниц в оперативной памяти будет располагаться по адресу 1Мб (100000h).

Теперь секундочку внимания! В нашем примере единственное, что мы делаем полезного - выводим на экран некоторую надпись. По сути, при этом используется один единственный адрес - ES:ESI, или 0B8000h. Следовательно, в данном примере для демонстрации возможностей страничной адресации достаточно заполнить лишь ОДИН ЕДИНСТВЕННЫЙ элемент каталога страниц. Так и сделаем:

Код (Text):
  1.  
  2.         mov  EDI,100000h    ; начало каталога страниц (1Мб)
  3.         mov  EAX,101007h    ; это наш один единственный значащий элемент
  4.         stosd               ; ES:[EDI] <- EAX
  5.         mov  ECX,1023       ; остальные 1023 элементов
  6.         xor  EAX,EAX       
  7.         rep  stosd      ; забьем нулями все остальные элементы каталога

кстати, почему такой загадочный элемент - 101007h? Открываем формат элемента каталога страниц и все становится ясно. (не ленимся, не ленимся! Открываем 7 выпуск и смотрим на картинку). 7 (установлены младшие 3 бита адреса) - означает, что страница присутствует в оперативной памяти (бит P), доступна для чтения записи (бит R/W) и доступна с любого уровня привилегий (бит U/S). А что такое 1010? Не забыл, что младшие 12 бит для АДРЕСА таблицы страниц ВСЕГДА РАВНЫ нулю? Значит, это 1010000h, что соответствует 1Мб + 4Кб, а 4 Кб - потому что САМ каталог занимает столько.

все. С каталогом покончено. Теперь примемся за ТАБЛИЦУ СТРАНИЦ

Код (Text):
  1.  
  2.         mov  EAX,00000007h  ; первая запись - адрес нулевой страницы равен 0
  3.         mov  ECX,1024       ; кол-во страниц в таблице
  4.  
  5. fill_page_table:
  6.         stosd               ; запишем первый элемент
  7.         add  EAX,1000h      ; добавим 4 Кб
  8.         loop     fill_page_table    ; и повторим для всех элементов таблицы страниц
  9.    
  10.         mov  EAX,00100000h  ; базовый адрес = 1 Мб
  11.         mov  CR3,EAX        ; в CR3 его! (база каталога страниц ВСЕГДА должна лежать в CR3)
  12.  
  13. ; включить страничную адресацию
  14.           mov    EAX,CR0
  15.         or   EAX,80000000h
  16.         mov  CR0,EAX
  17. ; а теперь изменить физический адрес страницы 12000h на 0B8000h
  18.           mov    EAX,000B8007h
  19.         mov  ES:00101000h+012h*4,EAX

вот здесь надо проникнуться всем существом. Чего это мы такого сотворили? А вот чего: мы, в таблице страниц, заменили адрес страницы, начало которой равно 12000h байт НА АДРЕС НАЧАЛА ВИДЕОПАМЯТИ (0B8000h)!!! Как это у нас получилось? Очень просто. АДРЕС НАЧАЛА ТАБЛИЦЫ СТРАНИЦ = 101000h, так? А сколько занимает один элемент? 4 байта!!! (32 бита). Поэтому 12*4 - это и есть ЗАПИСЬ ТАБЛИЦЫ СТРАНИЦ, которая указывает на страницу, начинающуюся по адресу 12000h!!!

А вот теперь демонстрация силы и мощи страничной адресации:

Код (Text):
  1.  
  2. ; вывод mes1 по стандартному адресу (начало видеопамяти 0B8000h)
  3.         mov  EDI,0B8000h    ; для команды movsw, EDI = начало видепамяти
  4.         mov      ESI,PM_DATA    
  5.         shl      ESI,4
  6.         add      ESI,offset mes1  ; ESI = адрес начала mes1
  7.         mov  ECX,mes_len      ; длина текста в ECX
  8.         rep      movsw           ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)
  9.  
  10. ; вывод mes2 по НЕСТАНДАРТНОМУ АДРЕСУ 12000h:
  11.         mov      EDI,0120A0h ; 12000h (уже можешь считать, что это 0B8000h) + A0h
  12.        mov       ESI,PM_DATA
  13.        shl       ESI,4
  14.        add       ESI,offset mes2     ; ESI = адрес начала mes2
  15.        mov       ECX,mes_len         ; длина текста в ECX
  16.        rep       movsw               ; DS:ESI (наше сообщение) -> ES:

А теперь небольшое партийное задание: переделать прогу так, чтобы начало видеопамяти совпадало с адресом 66000h.

Ответы присылать на brokensword@mail.ru

Кто разберется и пришлет правильный ответ - тот проникся страничной адресацией.

Напоследок - код всей проги (запускать ТОЛЬКО в реальном режиме :smile3:

Код (Text):
  1.  
  2. ; TASM:
  3. ; TASM /m PM.asm
  4. ; TLINK /x /3 PM.obj
  5. ; PM.exe
  6.  
  7. ; MASM:
  8. ; ML /c PM.asm
  9. ; LINK PM.obj,,NUL,,,
  10. ; PM.exe
  11.  
  12.         .386p                                           ; разрешить привилегированные инструкции i386
  13.        
  14. ; СЕГМЕНТ КОДА (для Real Mode)
  15. ; ----------------------------------------------------------------------------------
  16. RM_CODE     segment     para public 'CODE' use16
  17.         assume      CS:RM_CODE,SS:RM_STACK
  18. @@start:
  19.                     mov                 AX,03h
  20.                     int                 10h            ; текстовый режим 80x25 + очистка экрана
  21.                
  22. ; открываем линию А20 (для 32-х битной адресации):
  23.         in      AL,92h
  24.         or      AL,2
  25.         out     92h,AL
  26.  
  27. ; вычисляем линейный адрес метки ENTRY_POINT (точка входа в защищенный режим):
  28.         xor     EAX,EAX             ; обнуляем регистра EAX
  29.         mov     AX,PM_CODE          ; AX = номер сегмента PM_CODE
  30.         shl     EAX,4               ; EAX = линейный адрес PM_CODE
  31.         add     EAX,offset ENTRY_POINT      ; EAX = линейный адрес ENTRY_POINT
  32.         mov     dword ptr ENTRY_OFF,EAX     ; сохраняем его в переменной    
  33. ; (кстати, подобный "трюк" называется SMC или Self Modyfing Code - самомодифицирующийся код)
  34.  
  35. ; теперь надо вычислить линейный адрес GDT (для загрузки регистра GDTR):
  36.         xor     EAX,EAX
  37.         mov     AX,RM_CODE          ; AX = номер сегмента RM_CODE
  38.         shl     EAX,4               ; EAX = линейный адрес RM_CODE
  39.         add     AX,offset GDT           ; теперь EAX = линейный адрес GDT
  40.  
  41. ; линейный адрес GDT кладем в заранее подготовленную переменную:
  42.         mov     dword ptr GDTR+2,EAX
  43. ; а подобный трюк назвать SMC уже нельзя, потому как по сути мы модифицируем данные <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3    :smile3:">
  44.  
  45. ; собственно, загрузка регистра GDTR:
  46.         lgdt        fword ptr GDTR
  47.  
  48. ; запрет маскируемых прерываний:
  49.         cli
  50.  
  51. ; запрет немаскируемых прерываний:
  52.         in      AL,70h
  53.         or      AL,80h
  54.         out     70h,AL
  55.  
  56. ; переключение в защищенный режим:
  57.         mov     EAX,CR0
  58.         or      AL,1
  59.         mov     CR0,EAX
  60.  
  61. ; загрузить новый селектор в регистр CS
  62.         db      66h             ; префикс изменения разрядности операнда
  63.         db      0EAh                ; опкод команды JMP FAR
  64. ENTRY_OFF   dd      ?               ; 32-битное смещение
  65.         dw      00001000b           ; селектор первого дескриптора (CODE_descr)
  66.  
  67. ; ТАБЛИЦА ГЛОБАЛЬНЫХ ДЕСКРИПТОРОВ:
  68. GDT:  
  69. ; нулевой дескриптор (обязательно должен присутствовать в GDT!):
  70. NULL_descr  db      8 dup(0)
  71. CODE_descr  db      0FFh,0FFh,00h,00h,00h,10011010b,11001111b,00h
  72. DATA_descr  db      0FFh,0FFh,00h,00h,00h,10010010b,11001111b,00h
  73. GDT_size    equ         $-GDT               ; размер GDT
  74.  
  75. GDTR        dw      GDT_size-1          ; 16-битный лимит GDT
  76.         dd      ?               ; здесь будет 32-битный линейный адрес GDT
  77. RM_CODE         ends
  78. ; -----------------------------------------------------------------------------
  79.  
  80.  
  81.  
  82. ; СЕГМЕНТ СТЕКА (для Real Mode)
  83. ; -----------------------------------------------------------------------------
  84. RM_STACK       segment          para stack 'STACK' use16
  85.             db     100h dup(?)         ; 256 байт под стек - это даже много
  86. RM_STACK       ends
  87. ; -----------------------------------------------------------------------------
  88.  
  89.  
  90.  
  91. ; СЕГМЕНТ КОДА (для Protected Mode)
  92. ; -----------------------------------------------------------------------------
  93. PM_CODE     segment     para public 'CODE' use32
  94.         assume      CS:PM_CODE,DS:PM_DATA
  95. ENTRY_POINT:
  96. ; загрузим сегментные регистры селекторами на соответствующие дескрипторы:
  97.                  mov           AX,00010000b      ; селектор на второй дескриптор (DATA_descr)
  98.      mov           DS,AX                         ; в DS его        
  99.      mov           ES,AX                         ; его же - в ES
  100.  
  101. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  102. ; создать каталог страниц
  103.                 mov        EDI,00100000h               ; его физический адрес - 1 Мб
  104.     mov        EAX,00101007h               ; адрес таблицы 0 = 1 Мб + 4 Кб
  105.     stosd                              ; записать первый элемент каталога
  106.     mov        ECX,1023                    ; остальные элементы каталога -
  107.     xor        EAX,EAX                     ; нули
  108.     rep                 stosd
  109. ; заполнить таблицу страниц 0
  110.                 mov        EAX,00000007h               ; 0 - адрес страницы 0
  111.     mov        ECX,1024                    ; число страниц в таблице
  112. fill_page_table:
  113.     stosd                              ; записать элемент таблицы
  114.     add        EAX,00001000h               ; добавить к адресу 4096 байтов
  115.     loop                fill_page_table        ; и повторить для всех элементов
  116. ; поместить адрес каталога страниц в CR3
  117.                 mov        EAX,00100000h               ; базовый адрес = 1 Мб
  118.     mov        CR3,EAX
  119. ; включить страничную адресацию,
  120.                 mov        EAX,CR0
  121.  or        EAX,80000000h
  122.      mov           CR0,EAX
  123. ; а теперь изменить физический адрес страницы 12000h на 0B8000h
  124.                 mov        EAX,000B8007h
  125.     mov        ES:00101000h+012h*4,EAX
  126. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  127. ; вывод mes1 по стандартному адресу (начало видеопамяти 0B8000h)
  128.     mov        EDI,0B8000h                 ; для команды movsw, EDI = начало видепамяти
  129.     mov            ESI,PM_DATA                 ;
  130.     shl            ESI,4
  131.     add            ESI,offset mes1             ; ESI = адрес начала mes1
  132.      mov           ECX,mes_len                 ; длина текста в ECX
  133.     rep            movsw                       ; DS:ESI (наше сообщение) -> ES:EDI
  134.                                                ; (видеопамять)
  135.  
  136. ; вывод mes2 по НЕСТАНДАРТНОМУ АДРЕСУ 12000h:
  137.                 mov            EDI,0120A0h     ; 12000h (уже можешь считать, что это
  138.                                                ; 0B8000h) + A0h
  139.                 mov            ESI,PM_DATA
  140.                 shl            ESI,4
  141.                 add            ESI,offset mes2 ; ESI = адрес начала mes2
  142.                 mov            ECX,mes_len     ; длина текста в ECX
  143.                 rep            movsw           ; DS:ESI (наше сообщение) -> ES:12000h
  144.                                                ;(типа видеопамять)
  145.  
  146.     jmp            $                           ; погружаемся в вечный цикл
  147. PM_CODE         ends
  148. ; -------------------------------------------------------------------------------------
  149.  
  150.  
  151. ; СЕГМЕНТ ДАННЫХ (для Protected Mode)
  152. ; -------------------------------------------------------------------------------------
  153. PM_DATA         segment        para public 'DATA' use32
  154.         assume         CS:PM_DATA
  155.  
  156. ; сообщение, которое мы будем выводить на экран (оформим его в виде блока повторений irpc):
  157. mes1:
  158. irpc            mes1,          ; &lt;This string was outputted to standart adress 0B8000h...&gt;
  159.                  db            '&mes1&',0Dh
  160. endm
  161. mes2:
  162. irpc            mes2,          ;&lt;And this one - to dummy adress 0120A0h. Cool?
  163.                                ; Now press RESET...&gt;
  164.                 db             '&mes2&',0Bh
  165. endm
  166. mes_len         equ            66                  ; длина в байтах
  167. PM_DATA         ends
  168. ; ----------------------------------------------------------------------------------------------  
  169.                 end         @@start
© Broken Sword

0 1.571
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532