Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Процессор INTEL в защищенном режиме


Информационный Канал Subscribe.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 (итого у нас останется два дескриптора – кода и данных). Для чего – станет ясно ниже.

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

ENTRY_POINT:
; загрузим сегментные регистры селекторами на соответствующие дескрипторы:

           mov  AX,00010000b    ; селектор на второй дескриптор (DATA_descr)
      mov  DS,AX           ; в DS его
     mov  ES,AX            ; и в ES его же

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

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

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

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

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



      mov  EAX,00000007h  ; первая запись – адрес нулевой страницы равен 0
     mov  ECX,1024  ; кол-во страниц в таблице

fill_page_table:
      stosd    ; запишем первый элемент
      add  EAX,1000h  ; добавим 4 Кб
      loop  fill_page_table ; и повторим для всех элементов таблицы страниц

      mov  EAX,00100000h  ; базовый адрес = 1 Мб
     mov  CR3,EAX  ; в CR3 его! (база каталога страниц ВСЕГДА должна лежать в CR3)

; включить страничную адресацию
           mov  EAX,CR0
      or  EAX,80000000h
      mov  CR0,EAX
; а теперь изменить физический адрес страницы 12000h на 0B8000h
           mov  EAX,000B8007h
      mov  ES:00101000h+012h*4,EAX

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

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

; вывод mes1 по стандартному адресу (начало видеопамяти 0B8000h)
      mov  EDI,0B8000h     ; для команды movsw, EDI = начало видепамяти
      mov      ESI,PM_DATA
      shl      ESI,4
      add      ESI,offset mes1   ; ESI = адрес начала mes1
      mov  ECX,mes_len       ; длина текста в ECX
      rep      movsw            ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)

; вывод mes2 по НЕСТАНДАРТНОМУ АДРЕСУ 12000h:
         mov      EDI,0120A0h   ; 12000h (уже можешь считать, что это 0B8000h) + A0h
         mov      ESI,PM_DATA
         shl      ESI,4
         add      ESI,offset mes2      ; ESI = адрес начала mes2
         mov      ECX,mes_len          ; длина текста в ECX
         rep      movsw                ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)

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

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

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

Напоследок – код всей проги (запускать ТОЛЬКО в реальном режиме :) Качать здесь: PM2.asm



; TASM:
; TASM /m PM.asm
; TLINK /x /3 PM.obj
; PM.exe

; MASM:
; ML /c PM.asm
; LINK PM.obj,,NUL,,,
; PM.exe

  .386p                                           ; разрешить привилегированные инструкции i386

; СЕГМЕНТ КОДА (для Real Mode)
; ---------------------------------------------------------------------------------------------------------
RM_CODE  segment  para public 'CODE' use16
  assume   CS:RM_CODE,SS:RM_STACK
@@start:
                mov             AX,03h
                int             10h                             ; текстовый режим 80x25 + очистка экрана

; открываем линию А20 (для 32-х битной адресации):
  in  AL,92h
  or  AL,2
  out  92h,AL

; вычисляем линейный адрес метки ENTRY_POINT (точка входа в защищенный режим):
  xor  EAX,EAX    ; обнуляем регистра EAX
  mov  AX,PM_CODE   ; AX = номер сегмента PM_CODE
  shl  EAX,4    ; EAX = линейный адрес PM_CODE
  add  EAX,offset ENTRY_POINT   ; EAX = линейный адрес ENTRY_POINT
  mov  dword ptr ENTRY_OFF,EAX  ; сохраняем его в переменной
; (кстати, подобный "трюк" называется SMC или Self Modyfing Code - самомодифицирующийся код)

; теперь надо вычислить линейный адрес GDT (для загрузки регистра GDTR):
  xor  EAX,EAX
  mov  AX,RM_CODE   ; AX = номер сегмента RM_CODE
  shl  EAX,4    ; EAX = линейный адрес RM_CODE
  add  AX,offset GDT   ; теперь EAX = линейный адрес GDT

; линейный адрес GDT кладем в заранее подготовленную переменную:
  mov  dword ptr GDTR+2,EAX
; а подобный трюк назвать SMC уже нельзя, потому как по сути мы модифицируем данные :)

; собственно, загрузка регистра GDTR:
  lgdt  fword ptr GDTR

; запрет маскируемых прерываний:
  cli

; запрет немаскируемых прерываний:
  in  AL,70h
  or  AL,80h
  out  70h,AL

; переключение в защищенный режим:
  mov  EAX,CR0
  or  AL,1
  mov  CR0,EAX

; загрузить новый селектор в регистр CS
  db  66h    ; префикс изменения разрядности операнда
  db  0EAh    ; опкод команды JMP FAR
ENTRY_OFF  dd  ?    ; 32-битное смещение
  dw  00001000b   ; селектор первого дескриптора (CODE_descr)

; ТАБЛИЦА ГЛОБАЛЬНЫХ ДЕСКРИПТОРОВ:
GDT:
; нулевой дескриптор (обязательно должен присутствовать в GDT!):
NULL_descr db  8 dup(0)
CODE_descr db  0FFh,0FFh,00h,00h,00h,10011010b,11001111b,00h
DATA_descr db  0FFh,0FFh,00h,00h,00h,10010010b,11001111b,00h
GDT_size equ   $-GDT    ; размер GDT

GDTR  dw  GDT_size-1   ; 16-битный лимит GDT
  dd  ?    ; здесь будет 32-битный линейный адрес GDT
RM_CODE         ends
; ---------------------------------------------------------------------------------------------------------



; СЕГМЕНТ СТЕКА (для Real Mode)
; ---------------------------------------------------------------------------------------------------------
RM_STACK        segment         para stack 'STACK' use16
         db     100h dup(?)                     ; 256 байт под стек - это даже много
RM_STACK        ends
; ---------------------------------------------------------------------------------------------------------



; СЕГМЕНТ КОДА (для Protected Mode)
; ---------------------------------------------------------------------------------------------------------
PM_CODE  segment  para public 'CODE' use32
  assume  CS:PM_CODE,DS:PM_DATA
ENTRY_POINT:
; загрузим сегментные регистры селекторами на соответствующие дескрипторы:
                mov        AX,00010000b                ; селектор на второй дескриптор (DATA_descr)
   mov        DS,AX                       ; в DS его
   mov        ES,AX                       ; его же - в ES

; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; создать каталог страниц
                mov        EDI,00100000h             ; его физический адрес - 1 Мб
  mov        EAX,00101007h             ; адрес таблицы 0 = 1 Мб + 4 Кб
  stosd                       ; записать первый элемент каталога
  mov        ECX,1023                     ; остальные элементы каталога -
  xor        EAX,EAX                     ; нули
  rep            stosd
; заполнить таблицу страниц 0
                mov        EAX,00000007h             ; 0 - адрес страницы 0
  mov        ECX,1024                     ; число страниц в таблице
fill_page_table:
  stosd                       ; записать элемент таблицы
  add        EAX,00001000h             ; добавить к адресу 4096 байтов
  loop           fill_page_table      ; и повторить для всех элементов
; поместить адрес каталога страниц в CR3
                mov        EAX,00100000h             ; базовый адрес = 1 Мб
  mov        CR3,EAX
; включить страничную адресацию,
                mov        EAX,CR0
   or        EAX,80000000h
   mov        CR0,EAX
; а теперь изменить физический адрес страницы 12000h на 0B8000h
                mov        EAX,000B8007h
  mov        ES:00101000h+012h*4,EAX
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; вывод mes1 по стандартному адресу (начало видеопамяти 0B8000h)
  mov        EDI,0B8000h                  ; для команды movsw, EDI = начало видепамяти
  mov            ESI,PM_DATA
  shl            ESI,4
  add            ESI,offset mes1              ; ESI = адрес начала mes1
  mov        ECX,mes_len                  ; длина текста в ECX
  rep            movsw                        ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)

; вывод mes2 по НЕСТАНДАРТНОМУ АДРЕСУ 12000h:
                mov            EDI,0120A0h                  ; 12000h (уже можешь считать, что это 0B8000h) + A0h
                mov            ESI,PM_DATA
                shl            ESI,4
                add            ESI,offset mes2              ; ESI = адрес начала mes2
                mov            ECX,mes_len                  ; длина текста в ECX
                rep            movsw                        ; DS:ESI (наше сообщение) -> ES:12000h (типа видеопамять)

  jmp            $                            ; погружаемся в вечный цикл
PM_CODE         ends
; ---------------------------------------------------------------------------------------------------------


; СЕГМЕНТ ДАННЫХ (для Protected Mode)
; ---------------------------------------------------------------------------------------------------------
PM_DATA         segment        para public 'DATA' use32
  assume        CS:PM_DATA

; сообщение, которое мы будем выводить на экран (оформим его в виде блока повторений irpc):
mes1:
irpc            mes1,          This string was outputted to standart adress 0B8000h...
                db             '&mes1&',0Dh
endm
mes2:
irpc            mes2,          And this one - to dummy adress 0120A0h. Cool? Now press RESET...
                db             '&mes2&',0Bh
endm
mes_len         equ            66             ; длина в байтах
PM_DATA         ends
; ---------------------------------------------------------------------------------------------------------
                end         @@start

© Broken Sword, 2002 - Рассылка

© Igoryk, 2002 - Дизайн




http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное