Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Textnew2

.pdf
Скачиваний:
13
Добавлен:
06.02.2018
Размер:
1.48 Mб
Скачать

BASEADR EQU 8000h segment .text

_start:cli push cs pop ds

lgdt [forgdt] ; загрузка регистра GDTR mov eax,cr0

bts eax,0 ; устанавливаем 0-й бит, который в CR0 наз. PE mov cr0,eax ; установка бита PE для защищенного режима ; процессор теперь в защищенном режиме !!!!

jmp word selcs: next

next: mov ax,selds

; загрузка селекторов для

mov ds,ax

; регистра DS

mov ax,selss

 

mov ss,ax

; регистра SS

mov ax,seles

 

mov es,ax

; регистра ES

wiwod: mov cx, lenmsg

; вывод сообщения о PMODE

mov di, 800

 

mov si, msg

 

call wiwtxt

 

dalee: jmp dalee

 

;----- end main program

 

wiwtxt: ; процедура вывода на экран - с тем же текстом, что и на рис. 7.4.1

 

. . .

 

 

ret ;;wiwtxt endp

 

endcode:

; метка конца сегмента команд

;;data segment

; сегмент данных

attr

db 1eh

; байт атрибутов - ярко-желтый на синем фоне

msg

db 'We are into protected mode!'

lenmsg equ $-msg ; длина сообщения msg

forgdt dw lengthgdt-1 ; информация для рег. GDTR; предел для 5x8 dd BASEADR+mgdt ; двойное слово для базового адреса GDT

selcs equ 8h

; селектор для регистра CS

selds equ 10h

; селектор для регистра DS

selss

equ 18h

; селектор для регистра SS

seles

equ 20h

; селектор для регистра ES

endata: ; метка конца сегмента данных

 

align 16

mgdt:

; начало глобальной таблицы дескрипторов

des_null dd 0,0 ; нулевой дескриптор должен быть пустым

151

des_cs dw endcode-1; ; дескриптор для регистра CS; поле для limit dw BASEADR

db 0, 98h, 0, 0

des_ds dw endata-1 ; дескриптор для регистра DS; поле для limit dw BASEADR

db 0, 92h, 0,0

des_ss dw 7C00h-1 ; дескриптор для регистра SS; поле для limit db 0,0,0, 92h, 0,0

des_es dw 3999 ; дескриптор для регистра ES; предел для длины 4000 db 00,80h,0bh, 92h, 0,0

lengthgdt equ $-mgdt

Рис. 7.5.1. Внесистемная программы выдачи сообщения в защищенном режиме

Эта программ после преобразование в двоичный код (формата bin для компилятора NASMW) должна быть подобно предыдущей программе записана как файл под именем OS2BOOT на загрузочную дискету, подготовленную соответствующим образом (как описывалось выше). С точки зрения внешнего функционирования, она всего лишь выдает сообщение о своей работе, выводя на экран текст, определенный в именованной области msg. Причем для такого вывода - путем непосредственного доступа к видеопамяти - применяется подпрограмм wiwtxt, образованная теми же командами, что и одноименная подпрограмма в предыдущей программе реального режима. Эта простенькая подпрограмма построена универсальным образом и может использоваться как для реального, так и для защищенного режима. Следует иметь в виду, что такая замечательная возможность имеется для подавляющего большинства программ. Исключением являются лишь программы, явно использующие средства системной архитектуры и программы, явно меняющие содержимое сегментных регистров.

В начале основной части программы сразу же запрещаются прерывания и выполняется настройка регистра DS на тот же начальный адрес, который задает регистр CS, т.е. на начальный адрес нашей программы при ее запуске. Далее выполняется команда lgdt [forgdt], которая осуществляет загрузку регистра GDTR. Операндом этой команды служит именованная область памяти forgdt, которая описана в программе как состоящая из двух полей, в первом из которых записана длина таблицы глобальных дескрипторов минус единица. Длина задается символическим обозначением lengthgdt, которое, в свою очередь, определяется через директиву EQU и адрес, следующий за упоминаемой таблицей.

Второе поле области forgdt, предназначенное для четырехбайтового адреса начала рабочей таблицы GDT, задано выражением BASEADR+mgdt. Символическая константа BASEADR определена численным значением действительного адреса для загрузки нашей программы в оперативную память. Эта загрузка должна осуществляться программой загрузочного сектора и (так было принято в загрузчике OS/2

152

и используется в его аналоге) этот адрес загрузчиком принудительно устанавливается именно как 8000h. Значением символического имени mgdt является его смещение от начала сегмента, формируемое транслятором. Читателю рекомендуется рассмотреть листинг работы транслятора над данной программой и вглядеться в значение, приписываемому строке программы с меткой mgdt, и в значение рассматриваемого второго поля области forgdt.

Чтобы программа могла работать, тем более работать правильно, сама таблица GDT должна быть сформирована по всем правилам и отвечать действительному размещению сегментов памяти, используемым при работе программы. В нашем примере начало этой таблицы обозначено меткой mgdt. Далее на языке ассемблера описываются 8-байтовые именованные области памяти, задающие дескрипторы с именами des_null, des_cs, des_ds, des_ss, des_es. Первый из этих дескрипторов по архитектурным соображениям всегда должен иметь нулевое содержимое (с целью автоматического "отлавливания" ошибочного заполнения сегментных регистров нулевым значением). Последующие дескрипторы предназначены для использования посредством номеров, соответственно, в сегментных регистрах cs, ds, ss и es. С учетом порядковых номеров этих дескрипторов в таблице mgdt, соответствующие будущие значения этих регистров задаются так называемыми селекторами, которые в нашем случае должны быть равны 8, 16, 24 и 32. Эти селекторы задаются для наглядности программисту символическими константами selcs, selds, selss, seles с помощью директивы EQU.

Дескриптор des_cs описывает сегмент, начинающийся с адреса BASEADR (действительного адреса размещения кодов команд программы в памяти послед загрузки). Практически это значение указано помещаемым только в два из четырех байтов дескриптора, предназначенных для размещения базового адреса сегмента. В нашем случае младших байтов оказывается достаточным для правильного задания адреса, так как этот адрес имеет отличные от нуля биты только для этих байтов, а в остальных байтах для базового адреса (4-го и 7-го) явно задаются нулевые значения. В качестве значения максимального смещения в сегменте машинных команд берется значение метки endcode, уменьшенное на единицу, причем эта метка записана как раз за областью всех команда данной программы. В качестве значения байта AR задана константа 98h, предназначенная, как уже объяснялось, именно для сегментов команд.

В нашем простейшем примере дескриптор памяти des_ds для сегмента данных задается не с полным использованием возможностей защиты. Базовый адрес в нем задается как начало всей нашей программы (хотя в начале программы размещаются коды команд, которые было бы надежней сделать недоступными для доступа и изменения как данные). Рассмотренный вариант использован для упрощения первого демонстрационного примера. В качестве значения поля предела дескриптора использовано значение метки endata, из которого вычитается единица, где сама метка отмечает конец области данных в программе. Байт доступа задан константой 92h, предназначенной для определения доступа к области данных с возможностью записи в эту область командами текущей программы.

153

Для дескриптора des_ss стека в качестве значения базового адреса использована нулевая константа. Здесь использована информация, что программа загрузочного сектора устанавливает сегмент стека с самого начала памяти до границы в 28 Кбайт, т.е. его начальный адрес равен нулю, а предельное смещение на единицу меньше константы 7C00h.

Дескриптор des_es описывает сегмент данных видеопамяти, которым программа будет пользоваться для вывода текстов на экран. В качестве базового адреса здесь используется известная техническая информация, что начальный адрес этой памяти есть 0B8000h, а ее размер для стандартного текстового режима имеет величину 25*80*2. Заметим, что для данного дескриптора приходится задавать не нулевыми уже три байта базового адреса и значение этих байтов задаются так, что младшие байты адреса размещаются в байтах памяти с меньшим адресом. Последнее является характерной особенностью всей архитектуры процессоров Intel: все константы размещаются в памяти так, что младшие байты констант занимают младшие байты памяти. (Об этом вскользь упоминалось в разделе 2.2 при обсуждении примеров директив задания констант, но тогда эта проблема не была "злободневной".) Напомним теперь еще раз, что запись заполнения памяти

DD 123456h равносильно записи

DB 56h, 34h, 12h, 00h

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

Байты прав доступа для последних двух дескрипторов задаются константами 92h, т.е. разрешают как чтение, так и запись данных в сегмент памяти.

После занесения в регистр GDTR необходимой информации о таблице GDT, программа выполняет установка в единицу младшего бита регистра CR0. Как уже подчеркивалось ранее, установка отдельного бита в регистре, где остальные биты несут свои существенные функции, необходимо осуществлять так, чтобы не изменить эти другие биты. Поэтому в программе вначале содержимое регистра CR0 переносится в регистр EAX, затем специальной командой установки битов BTS устанавливается в единицу младший бит и это новое значение переносится в регистр CR0. Заметим, что для операций с регистром CR0 архитектура Intel предоставляет только возможности команды MOV, причем другим операндом таких команд может быть только регистр EAX (этого оказывается вполне достаточным).

Заметим, что имя команды BTS является сокращением BiT Set, а ее возможности достаточно велики, но в данном тексте обсуждаться не будут. Альтернативой использованной установки младшего бита (вместо команды bts eax,0) является использование команды

OR eax, 1

или даже OR AL,1.

154

После установки в единицу младшего бита регистра CR0, процессор работает в защищенном режиме и автоматически должен использовать содержимое сегментных регистров для доступа к дескрипторам памяти. Но в этих регистрах для рассматриваемой программы после такой установки еще нет никаких селекторов. Если с селекторами для сегментов данных можно еще повременить, то селектор дескриптора для доступа к следующей команды кажется совершенно необходимым. К счастью, пока процессор не встретит команду изменения линейной последовательности для извлечении команд из памяти, то он использует машинные коды, предварительно помещенные из этой памяти во внутренний кэш. В архитектуре Intel отсутствуют команды, явно изменяющие значение регистров CS и EIP (случайное применение который было бы очень опасно для нормального функционирования программ). С другой стороны, эти регистры неизбежно изменяются при выполнении дальних переходов в вызываемые подпрограммы и дальних безусловных переходов.

В ассемблера NASM для наших целей подойдет команда JMP WORD SELCS : NEXT

которая обеспечивает замену содержимого регистра CS на значение параметра SELCS, а содержимое регистра IP на значение параметра NEXT. Именно эта команда использована в нашей программе для указанных целей. Заметим, что для более общего случая смещения в программе, превосходящего 16-битное значение, в данном ассемблере служит мнемонически задаваемая команда, в качестве второго служебного слова которого следует указать DWORD. Она заносит значение операнда, заданного после двоеточия, в регистр EIP.

(В более распространенных, но менее профессиональных, ассемблерах TASM и MASM для только что рассмотренных команд дальних безусловных переходов отсутствуют явные средства. Единственной возможностью задать шестнадцатеричный код таких команд заключается здесь в использовании директив явного задания кодов как данных. Так команда дальнего перехода может быть задана на этих ас-

семблерах в виде

 

 

db 0eah

; код операции для jmp FAR next

dw next

; смещение для

jmp FAR next

dw selcs

; селектор для

jmp FAR next

что, конечно, очень не наглядно и достаточно неудобно.)

Далее в командах с метки next производится занесение в регистры DS, SS, ES селекторов selds, selss, seles, которые содержат индексы подготовленных нами дескрипторов для работы с соответствующими сегментами памяти. Эти занесения производятся обычными командами пересылки. Затем подготавливается и осуществляется вызов подпрограммы wiwtxt. Заметим, что эта подготовка ничем не отличается от использованной при вызове той же подпрограммы в реальном режиме. Обычным образом в регистр CX заносится число выводимых символов, в регистр - SI смещение текста в сегменте данных, а в DI - смещение в области видеопамяти. По существу ситуация отличается тем, что в регистре ES теперь содержится селектор (значение номера дескриптора) для области видеопамяти, а в реальном режиме

155

этот регистр содержал сегментный адрес этой области. Но установка содержимого регистра ES выполняется предварительно и после такой установки действия, использующие только смещения в областях и сегментах памяти, не содержат ничего нового и особенного. После вывода сообщения программа зацикливается на метке dalee.

Еще одной, но несущественной особенностью программы является использование директивы align, аргумент которой задает на целую кратность какой величине следует установить адрес следующего поля. В данном случае применение этой директивы исключительно вспомогательное, но помогает по листингу проще замечать ошибки описания отдельных дескрипторов, если по невнимательности программист пропустит описание какого-то байта или поля. (По листингу проще будет заметить, что очередной дескриптор или его завершение начинается с границы, последняя шестнадцатеричная цифра которой не совпадает с 0 или 8).

7.6. Обработка прерываний в защищенном режиме

При использовании защищенного режима система прерывания работает иначе рассмотренному выше. Вместо таблицы векторов в защищенном режиме применяется таблица дескрипторов прерываний. Для ее фактического использования требуется занесение данных об этой таблицы в специальный регистр IDTR. Аналогично регистру GDTR, регистр IDTR содержит шесть байтов, в младших двух из которых содержится максимальное допустимое смещение внутри таблицы дескрипторов прерываний (размер таблицы минус единица). Старшие байты содержат действительный адрес таблицы дескрипторов прерываний в памяти. Для заполнения регистра IDTR служит команда LIDT, операндом которого служит 6-байтовая область данных в памяти.

Дескрипторы, составляющая таблицу IDT также 8-байтовые, 5-й байт их своим кодом определяет, что дескрипторы предназначены для системы прерываний. Всего для этих целей предназначены 4 варианта кодов, равные 86h, 87h, 8Eh и 8Fh. Различия вариантов с этими кодами, имеющими нулевые и единичные младшие биты достаточно тонкие. Дескрипторы с кодами 87h и 8Fh называются дескрипторами вентилей ловушки и отличаются от своих аналогов (с кодами 86h и 8Eh) тем, что при их использовании аппаратура не сбрасывает автоматически флаги прерываний в регистре EFLAGS. С ознакомительными целями мы ограничимся только дескрипторами с кодами типа 86h и 8Eh. Использование первого из них вызывает автоматическое запоминание (внутренней процедурой прерывания) в стеке содержимого регистров FLAGS, CS и IP (именно в этом порядке и как 16-ти битных кодов). Дескрипторы прерываний с кодом типа 8Eh вызывают автоматическое запоминание в стеке содержимого регистров EFLAGS, CS и EIP, причем содержимое регистра CS запоминается также как четырехбайтовое значение. Вариант кода типа 86h относится, по существу, к 16-битной архитектуре, а вариант типа 8Eh предназначен для полноценного использования 32-битной архитектуры.

156

Дескрипторы прерываний включают значение смещения обработчика прерывания, которое должно размещаться в 0-м, 1-м, 6-м и 7-м байтах, и значение селектора для области машинных команд обработчика прерываний, которое должно помещаться в 2-м и 3-м байтах дескриптора. Неиспользуемый байт дескриптора должен быть нулевым (4-й байт).

Для внутренних прерываний защищенного режима зарезервировано значительно больше номеров прерываний, чем в реальном режиме. Из этих номеров наиболее важным в применении является 13-й. Ему соответствует так называемое общее нарушение защиты. Таким нарушением считается обращение к данным вне пределов используемого сегмента памяти. В частности попытка читать данные за пределам сегмента данных. Сюда же относится попытка записать данные в сегмент, который определен в соответствующем дескрипторе, как не допускающий запись данных. Нарушением общей защиты является и попытка выполнения программы за пределами текущего сегмента памяти машинных команд и множество других ошибочных или нежелательных ситуаций.

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

Далее на рис. 7.6.1 приведена программа демонстрации системы прерываний защищенного режима. Подобно уже рассмотренным в предыдущих разделах программам, данная программа должна быть запущена загрузчиком из загрузочного сектора дискеты и для этого быть записана в форме исполняемой программы как файл с именем OS2BOOT на этой дискете.

BASEADR EQU 8000h segment .text

_start:cli push cs pop ds

lgdt [forgdt] ; загрузка регистра GDTR lidt [foridt] ; загрузка регистра IDTR mov eax,cr0

bts eax,0 ; устанавливаем 0-й бит, который в CR0 будет PE mov cr0,eax ; установка бита PE для защищенного режима

; процессор теперь в защищенном режиме !!!!

jmp word selcs: next

 

next: mov ax,selds

; загрузка селекторов для

mov ds,ax

; регистра DS

mov ax,selss

 

157

mov ss,ax

; регистра SS

mov ax,seles

 

mov es,ax

; регистра ES

wiwod: mov cx, lenmsg ; вывод сообщения о PMODE mov di, 800

mov si, msg call wiwtxt

int 5 ; обращаемся к обработчику прерывания с номером 5 mov al,11111101b ; разрешаем маской аппаратные прерывания

out 21h,al

; только для клавиатуры

sti

 

waitf: cmp byte [flagexit], 0

je waitf

 

mov ebx, 400000

mov BYTE [ebx],7 ; обращение к данным за пределом сегмента dalee: jmp dalee

;----- end main program

wiwtxt:

; процедура вывода на экран - с тем же текстом, что и на рис. 7.4.1

. . .

ret ;;wiwtxt endp

interp9: ; обработчик прерываний с номером 9

pusha

 

in al, 60h

; читаем скан-код из порта 60h

test al,80h

; проверяем не сигнал ли это отпускания клавиши

jnz dal

 

cmp al, 1

 

jne noexit

mov byte [flagexit],1

noexit:

mov cx, lenmsg9

mov di, [posi]

mov si, msgint9

call wiwtxt ; вывод сообщения MSGINT9 о нажатии клавиши add WORD [posi], 30

; подготовить номер позиции экрана для след. сообщ.

cmp WORD [posi], 4000-30; если вышли за пределы экрана - jl dal

mov WORD [posi],0 ; то с его начала

; завершение обработчика аппаратного прерывания от клавиши

dal: mov al,20h

; кодом команды End_Of_Interrupt = 20h

out 20h,al

; сбросить обработанный сигнал в КНП

158

popa

iret ;; endp interp9

interp: ; proc near ; процедура для вывода на экран сообщения

. . . текст программы сообщения о прерывании, совпадающий

. . . с текстом одноименной программы с рис. 7.4.1

interp13: ; процедура вывода сообщения о нарушении общей защиты push ebp

mov ebp, esp

mov word [begpos], 160 ; начало 1-й строки экрана mov esi, msg_eip ;offset

mov di, [begpos] mov ecx, 4

mov BYTE [attr], 0Ch ; red on black

call wiwtxt ; вывод на экран текста 'EIP=' add word[begpos],8

mov eax, [ebp+8] ; в стеке на [ebp+8] - значение EIP call writedword ; к процедуре для вывода EAX mov esi, msg_cs ; offset

mov di, [begpos] mov ecx, 4

call wiwtxt ; вывод на экран текста ' CS=' add word [begpos],8

mov ax, [ebp+12] ; в стеке на [ebp+12] - значение CS call writeword ; к процедуре для вывода AX

pop ebp

add esp,16 ; убрать из стека код ошибки, EIP, CS, EFLAGS jmp dalee

; метка DALEE должна находиться в основной программе ;;interp13 endp

writedword: ;; proc ror eax,16 call writeword ror eax,16 call writeword

ret ;writedword endp writeword: ;proc

ror ax,8

call writehex ror ax,8

call writehex

159

 

ret ;;writeword endp

 

writehex:

; proc

 

 

 

push dx

 

 

 

ror al,4

 

 

 

call writesemibyte

 

 

ror al,4

 

 

 

call writesemibyte

 

 

pop dx

 

 

 

ret ;writehex endp

 

writesemibyte: ;proc

 

 

pusha

 

 

 

mov dl,al

 

 

 

and dl,0fh

 

 

 

cmp dl,10

 

 

 

jge hex

 

 

 

add dl,'0'

 

 

 

jmp wiwo

 

 

hex:

add dl,'A'-10

 

wiwo:mov di, [begpos]

 

 

add word [begpos],2

 

 

mov [es:di],dl

 

 

mov al,[attr]

 

 

 

mov BYTE [es:di+1], al

 

 

popa

 

 

 

 

ret ;;writesemibyte endp

 

endcode:

 

; метка конца сегмента команд

;;data segment

; сегмент данных

 

; информация для регистра IDTR в защищенном режиме

foridt dw 14*8-1

; для 14-ти дескрипторов прерывания

 

dd BASEADR+idt

;

attr

db 1eh

; байта атрибутов: ярко-желтый на синем фоне

msg

db 'We are into protected mode!'

lenmsg equ $-msg ; длина сообщения msg

posi

dw 1920 ; поз. экрана, с кот. вывод сообщ. о наж. клавиши

msgint9 db 'Pressed key! '

 

lenmsg9 equ $-msgint9

 

msgintp db 'Interrupt is!'

 

lenmsgp equ $-msgintp

 

begpos

dw 1600

 

msg_eip DB 'EIP=' msg_cs DB ' CS=' flagexit db 0

160

Соседние файлы в предмете Операционные системы