Задание для самостоятельного выполнения

Лабораторная работа №4. Защищенный режим работы процессора

Цель и порядок работы

Цель работы – ознакомиться с защищенным режимом работы процессора и научиться разрабатывать программы, используя этот режим.

Порядок выполнения работы:

- ознакомиться с описанием лабораторной работы;

- изучить возможности работы с защищённым режимом;

- получить задание у преподавателя;

- написать программу, ввести программу, отладить и решить ее на ЭВМ;

- оформить отчет.

Теоретические основы

 

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

- увеличение адресуемого пространства до 16 Мбайт для МП 286 и до 4 Гбайт для МП 386;

- возможность работать в виртуальном адресном пространстве, пре­вышающем максимально возможный объем физической памяти. Для МП 286 виртуальное пространство составляет 1 Гбайт, а для МП 386 и 486 – 64 Гбайт. Правда, для реализации виртуально­го режима необходимы, помимо дисков большой емкости, еще и соот­ветствующая операционная система, которая хранит все сегменты вы­полняемых программ в большом дисковом пространстве, автоматически загружая в оперативную память те или иные сегменты по мере необходимос­ти;

- организация многозадачного режима с параллельным выполнени­ем нескольких программ (процессов). Многоза­дачный режим организует многозадачная операционная система, однако, микропроцессор предоставляет необходимый для этого режима мощный и надежный механизм защиты задач друг от друга с помощью четырехуровневой системы привилегий;

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

При включении микропроцессора в нем автоматически устанавли­вается режим реального адреса. Переход в защищенный режим осу­ществляется программно путем выполнения соответствующей последо­вательности команд. Реальный и защищенный ре­жимы не совместимы!

Архитектура современного микропроцессора необычайно сложна. Столь же сложными оказываются и программы, использующие средства защищенного режима. Отдельные архитектурные особенности защищенного режима оказываются в достаточной степени замкнутыми и не зависящими друг от друга. Так, при работе в одноза­дачном режиме отпадает необходимость в изучении многообразных и замысловатых методов взаимодействия задач; во многих случаях можно отключить (или, точнее, не включать) механизм страничной организа­ции памяти; часто нет необходимости использовать уровни привилегий. Начнем изучение защищенного режима с рассмотрения простейшей программы, которая, будучи запущена обычным образом под управлением MS-DOS, переключает процессор в защищенный режим, выводит на экран для контроля несколько символов, переходит назад в реальный режим и завершается стандартным для DOS образом.

Перед за­пуском программ защищенного режима следует выгрузить драйверы об­служивания расширенной памяти (типа ЕММ386.ЕХЕ) и оболочку Windows.

Для проверки работы программы удобно использовать специальные программы для эмуляции аппаратного обеспечения различных платформ. Наиболее распрстранены такие виртуальные машины как VMware, Bochs, Parallels Workstation, QEMU, Virtual PC, VirtualBox. Данные виртуальные машины являются гипервизорами (эмуляторами аппаратного обеспечения) и не оеспечивают виртуализацию приложений, как например Java Virtual Machine. В качестве основы для проведения лабораторных работ предлагается свободнораспространяемая виртуальная машина qemu с установленной операционной системой MS DOS 6.22.

 

Пример 4.1 – Переход в защищенный режим и обратно

;В защищенном режиме вывод фиксированных символов на экран
IDEAL ; (1) Включение режима IDEAL
P386 ; (2) Разрешение трансляции всех, в том числе
; привилегированных команд МП 386 и 486
model small ; (3)
STACK 100h ; (4) Оперделение сегмента стека и его размера
;Структура для описания дескрипторов сегментов
struc descr ; (5)
limit dw 0 ; (6)
base_l dw 0 ; (7)
base_m db 0 ; (8)
attr_1 db 0 ; (9)
attr_2 db 0 ; (10)
base_h db 0 ; (11)
ends descr ; (12)
DATASEG ; (13) Начало сегмента данных
;Таблица глобальных дескрипторов GDT
gdt_null descr <0,0,0,0,0,0> ;(14) Селектор = 0
gdt_data descr <data_size-1,0,0,92h,0,0> ;(15) Селектор = 8
gdt_code descr <code_size-1,0,0,98h,0,0> ;(16) Селектор = 16
gdt_stack descr <100h-1,0,0,92h,0,0> ;(17) Селектор = 24
gdt_screen descr <4095,8000h,0Bh,92h,0,0> ;(18) Селектор = 32
gdt_size = $-gdt_null ;(19)
;Поля данных программы
pdescr dp 0 ;(20)
sym db 1 ;(21)
attr db 1Eh ;(22)
mes db 10,13,'Real mode',"$" ;(23)

data_size = $-gdt_null ;(24)
ends ;(25)
CODESEG ; (26) Начало сегмента команд
assume cs: @code, ds:@data ; (27)
sttt equ $ ; (28)
start: ; (29)
xor eax, eax ; (30)
mov ax, @data ; (31) Инициализация реального
mov ds, ax ; (32) режима

;Вычислим 32-битовый линейный адрес сегмента данных и загрузим его в дескриптор

;сегмента данных в таблице GDT. В регистре AX уже находится сегментный адрес.

;Умножим его на 16 сдвигом влево на 4 бита с размещением результата в регистрe EAX

shl eax, 4 ; (33) Сдвинем влево EАХ на 4 бита (равносильно умножению на 16)
; Теперь в EAX 32-битовый линейный адрес сегмента данных
mov ebp, eax ; (34) Сохраняем в EBP начало сегмента
mov bx, offset gdt_data ; (35) В ВХ адрес дескриптора
mov [(descr ptr bx).base_l], ax; (36) Загрузим младшую часть базы
rol eax, 16 ; (37) Поместим циклическим сдвгом старшие биты линейного адреса
; (находящиеся в битах [19...16] регистра EАХ) в AL
mov [(descr ptr bx).base_m], al; (38) Загрузим среднюю часть базы
;Вычислим 32-битовый линейный адрес сегмента команд и загрузим его
;в дескриптор сегмента команд в таблице глобальных дескрипторов
xor eax, eax ; (39) Очистка EAX
mov ax, cs ; (40) В AX - адрес сегмента команд
shl eax, 4 ; (41) Та же процедура умножения сегментного адреса на 16 сдвигом
mov bx, offset gdt_code ; (42) BX = адрес дескриптора
mov [(descr ptr bx).base_l], ax; (43) 3агруэка младшей части базы
rol eax, 16 ; (44)
mov [(descr ptr bx).base_m], al; (45) 3агруэка средней частей базы
;Аналогично для адреса сегмента стека
xor eax, eax ; (46)
mov ax, ss ; (47)
shl eax, 4 ; (48)
mov bx, offset gdt_stack ; (49) BX = адрес дескриптора стека
mov [(descr ptr bx).base_l], ax; (50)
rol eax, 16 ; (51)
mov [(descr ptr bx).base_m], al; (52)
;Подготовим псевдодескриптор pdescr и загрузим регистр GDTR
mov [dword ptr pdescr+2], ebp ; (53) Занесем в pdescr адрес GDT
; (совпадает с адресом начала сегмента данных)
mov [word ptr pdescr], gdt_size-1 ; (54) Граница GDT
lgdt [pword ptr pdescr] ; (55) Загрузим регистр GDTR
;Подготовимся к переходу в защищенный режим
cli ; (56) Запрет аппаратных прерываний
in al, 70h ; (57) Запрет немаскируемых прерываний (NMI) через
or al, 80h ; (58) индексный порт CMOS (70h) путем установки
out 70h, al ; (59) бита 7 (10000000b=80h)

jmp $+2 ; (60) Задержка (в данном случае необязательно)
;Переход в защищенный режим
;Слово состояния машины == младшее слово регистра CR0
mov eax, CR0 ; (61) Получим слово состояния машины
or eax, 1 ; (62) Установим бит РЕ
mov CR0, eax ; (63) Запишем назад слово состояния
;Теперь процессор работает в защищённом режиме
;Загружаем в CS:IP селектор:смещение точки continue и заодно очищаем очередь команд
db 0EAh ; (64) Код команды far jmp
dw offset continue ; (65) Смещение
dw 16 ; (66) Селектор сегмента команд
continue: ; (67)
;Делаем адресуемыми данные
mov ax, 8 ; (68) Селектор сегмента данных
mov ds, ax ; (69)
;Делаем адресуемым стек
mov ax, 24 ; (70) Селектор сегмента стека
mov ss, ax ; (71)
;Инициализируем ES селектором видеобуфера и выводим символы
mov ax, 32 ; (72) Селектор сегмента видеобуфера
mov es, ax ; (73)
;Подготовка к выводу на экран
mov bx, 800 ; (74) Начальное смещение на экране
mov cx, 640 ; (75) Число выводимым символов
mov ax, [word ptr sym] ; (76) Начальный символ с атрибутом
screen: ; (77)
mov [es:bx], ax ; (78) Вывод в видеобуфер
add bx, 2 ; (79) Сместимся в видеобуфере
inc ax ; (80) Следующий символ
loop screen ; (81) Цикл вывода на экран
;Вернемся в реальный режим
mov eax, CR0 ; (82) Получим слово состояния машины
and al, 0FEh ; (83) Сбросим бит PE
mov CR0, eax ; (84) Запишем назад слово состояния
;Теперь процессор работает в реальном режиме
;Загружаем в CS:IP селектор:смещение точки return
db 0EAh ; (85) Код команды far jmp
dw offset return ; (86) Смещение точки возврата
dw @code ; (87) Сегментный адрес сегмента кода

 

return: ; (88)
;Восстановим операционную среду реального режима
mov ax, @data ; (89) Восстановим адресуемость
mov ds, ax ; (90) данных
mov ax, @stack ; (91) Восстановим
mov ss, ax ; (92) адресуемость
mov sp, 100h ; (93) стека

;Разрешим аппаратные и немаскируемые прерывания
sti ; (94) Разрешение прерываний
in al, 70h ; (95)
and al, 07Fh ; (96) Сброс бита 7 в порте CMOS
out 70h, al ; (97) разрешение NMI

;Проверим выполнение функций DOS после возврата в реальный режим
mov ah, 09h ; (98) Функция вывода на экран строки
mov dx, offset mes ; (99) Адрес строки
int 21h ; (100) Выэов DOS

mov ax, 4C00h ; (101) Завершим программу обычным
int 21h ; (102) образом
ends ; (103) Конец сегмента команд
code_size=$-sttt ; (104) Размер сегмента команд
end start ; (105) Конец программы
end ; (106)

 

32-разрядные микропроцессоры отличаются от предыдущих расширенным набором команд, часть которых относится к привилегирован­ным. Для того чтобы разрешить транслятору обрабатывать эти команды, в текст программы необходимо включить директиву ассемблера P386.

Программа начинается с описания структуры дескриптора сегмента. В защищенном режиме для каждого сегмента программы (т.е. для сегментов команд, данных и стека) в программе должен быть определен дескриптор – 8-байтовое поле, в котором в определенном формате записываются базовый адрес сегмента, его длина и некоторые другие характеристики (рис. 4.1).

Рисунок 4.1 – Дескриптор сегмента.

Теперь для обращения к требуемому сегменту программист заносит в сегментный регистр не сегментный адрес, а так называемый селектор (рис. 4.2), в состав которого входит номер (индекс) соответствующего сегменту дескриптора. Процессор по этому номеру находит нужный де­скриптор, извлекает из него базовый адрес сегмента и, прибавляя к не­му указанное в конкретной команде смещение (относительный адрес), формирует адрес ячейки памяти. Индекс дескриптора (0, 1, 2 и т.д.) за­писывается в селектор начиная с бита 3, что эквивалентно умножению его на 8. Таким образом, можно считать, что селекторы последователь­ных дескрипторов представляют собой числа 0, 8, 16, 24 и т.д. (см. предложения 14…18).

Рисунок 4.2 –  Селектор дескриптора.

Структура descr предо­ставляет шаблон для дескрип­торов сегментов, об­легчающий их формирование. Сравнивая описание структу­ры descr в программе с рис. 6.1, нетрудно проследить их соответствие друг другу.

Граница (limit) сегмента представляет собой номер последнего байта сегмента. Так, для сегмента размером 375 байт граница равна 374. Поле границы состоит из 20 бит и разбито на две части. Млад­шие 16 бит границы занимают байты 0 и 1 дескриптора, а старшие 4 бита входят в байт атрибутов 2, занимая в нем биты 0…3. Получается, что размер сегмента ограничен величиной 1 Мбайт. На самом деле это не так. Граница может указываться либо в байтах (и тогда, действитель­но, максимальный размер сегмента равен 1 Мбайт), либо в блоках по 4 Кбайт (и тогда размер сегмента может достигать 4 Гбайт). В каких еди­ницах задается граница, определяет самый старший бит байта 7 (атрибуты 2). Этот бит называется битом дробности (гранулярности). Если он равен 0, граница указывается в байтах; если 1 - в блоках по 4 Кбайт.

База сегмента (32 бита) определяет начальный линейный адрес сег­мента в адресном пространстве процессора. Линейным называется ад­рес, выраженный не в виде комбинации сегмент:смещение, а просто номером байта в адресном пространстве. Казалось бы, линейный адрес - это просто другое название физического адреса. Для нашего примера это так и есть, в нем линейные адреса совпадают с физическими. Если, однако, в процессоре включен блок страничной организация памяти, то процедура преобразования адресов усложняется. Отдельные блоки раз­мером 4 Кбайт (страницы) линейного адресного пространства могут произвольным образом отображаться на физические адреса, в частности и так, что большие линейные адреса отображаются на начало фи­зической памяти и наоборот. Страничная адресация осуществляется аппаратно (хотя для ее включения требуются определенные программные усилия) и действует независимо от сегментной организации программы. Поэтому во всех программных структурах защищенного режима фигу­рируют не физические, а линейные адреса. Если страничная адресация выключена, эти линейные адреса совпадают с физическими, если включена – могут и не совпадать.

Страничная организация повышает эффективность использования памяти программами, однако, практически она имеет смысл лишь при выполнении больших по размеру задач, когда объем адресного про­странства задачи (виртуального адресного пространства) превышает на­личный объем памяти. В рассматриваемых в лабораторных работах примерах использу­ется чисто сегментная адресация без деления на страницы, и линейные адреса совпадают с физическими.

Поскольку в дескриптор записывается 32-битовый линейный базо­вый адрес (номер байта), сегмент в защищенном режиме может начинаться на любом байте, а не только на границе параграфа, и распо­лагаться в любом месте адресного пространства 4 Гбайт.

Поле базы, как и поле границы, разбито на 2 части: биты 0…23 за­нимают байты 2, 3 и 4 дескриптора, а биты 24…31 - байт 7. Для удобства программного обращения в структуре descr база описывается тремя полями: младшим словом (base_l) и двумя байтами: средним (base_m) и старшим (base_h).

В байте атрибутов 1 задастся рад характеристик сегмента. Не вдава­ясь пока в подробности этих характеристик, укажем, что в примере 4.1 используются сегменты двух типов: сегмент команд, для которого байт attr_1 должен иметь значение 98h, и сегмент данных (или стека) с кодом 92h.

Некоторые дополнительные характеристики сегмента указываются в старшем полубайте байта attr_2 (в частности, тип дробности). Для всех наших сегментов значение этого полубайта равно 0.

Сегмент данных @data, который для удобства изучения функциониро­вания программы расположен в начале программы, до сегмента команд, начинается с описания важнейшей системной структуры – таблицы гло­бальных дескрипторов. Как уже отмечалось выше, обращение к сегмен­там в защищенном режиме возможно исключительно через дескрипто­ры этих сегментов. Таким образом, в таблице дескрипторов должно быть описано столько дескрипторов, сколько сегментов использует программа. В нашем случае в таблицу включены, помимо обязательного нулевого дескриптора, всегда занимающего первое место в таблице, четыре дескриптора для сегментов данных, команд, стека и дополни­тельного сегмента данных, который мы наложим на видеобуфер, чтобы обеспечить возможность вывода в него символов. Порядок дескрипто­ров в таблице (кроме нулевого) не имеет значения.

Помимо единственной таблицы глобальных дескрипторов (она часто называется GDT от Global Descriptor Table) в памяти может нахо­диться множество таблиц локальных дескрипторов (LDT от Local Descriptor Table). Разница между ними в том, что сегменты, описы­ваемые глобальными дескрипторами, доступны всем задачам, выпол­няемым процессором, а к сегментам, описываемым локальными де­скрипторами, может обращаться только та задача, в которой эта де­скрипторы описаны. Поскольку пока мы имеем дело с однозадачным режимом, локальная таблица нам не нужна.

Поля дескрипторов для наглядности заполнены конкретными дан­ными явным образом, хотя объявление структуры descr с нулями во всех полях позволяет описать дескрипторы несколько короче, например:

gdt_null descr <> ;Селектор 0 - обязательный нулевой дескриптор

gdt_data descr <data_size-1,,,92h,,> ;Селектор 8 - сегмент данных

В дескрипторе gdt_data, описывающем сегмент данных программы, заполняется поле границы сегмента (фактическое значение размера сег­мента data_size будет вычислено транслятором, см. предложение 24), а также байт атрибутов 1. Код 92h говорит о том, что это сегмент данных с разрешением записи и чтения. База сегмента, т.е. физический адрес его начала, в явной форме в программе отсутствует, поэтому ее придет­ся программно вычислить и занести в дескриптор уже на этапе выпол­нения.

Дескриптор gdt_code сегмента команд заполняется схожим образом. Код атрибута 98h обозначает, что это исполняемый сегмент, к которо­му, между прочим, запрещено обращение с целью чтения или записи. Таким образом, сегменты команд в защищенном режиме нельзя моди­фицировать по ходу выполнения программы.

Дескриптор gdt_stack сегмента стека имеет, как и любой сегмент данных, код атрибута 92h, что разрешает его чтение и запись, и явным образом заданную границу – 255 байт (100h-1), что соответствует размеру стека. Базовый адрес сегмента стека так же придется вычислить на этапе вы­полнения программы.

Последний дескриптор gdt_screen описывает страницу 0 видеобуфе­ра. Размер видеостраницы, как известно, составляет 4096 байт, поэтому в поле границы указано число 4095. Базовый физический адрес страни­цы известен, он равен 0B8000h. Младшие 16 бит базы (число 8000h) за­полняют слово base_l дескриптора, биты 16…19 (число 0Bh) - байт base_m. Биты 20…31 базового адреса равны 0, поскольку видеобуфер размещается в первом мегабайте адресного пространства.

Перед переходом в защищенный режим процессору надо будет со­общить физический адрес таблицы глобальных дескрипторов и её раз­мер (точнее, границу). Размер GDT определяется на этапе трансляции в предложении 19.

Сегмент команд @code начинается, объявлением CODESEG. Этот описатель объявля­ет, что в данном сегменте будут по умолчанию использоваться 16-битовые адреса и операнды. Наша программа будет запускаться под управле­нием DOS, которая работает в реальном режиме с 16-битовыми адреса­ми и операндами. Указание описателя use16 не запрещает использовать в программе 32-битовые регистры.

Фактически вся программа примера 3.1, кроме ее завершающих строк, а также фрагмента, выполняемого в защищенном режиме, по­священа подготовке перехода в защищенный режим. Прежде всего, надо завершить формирование дескрипторов сегментов программы, в кото­рых остались незаполненными базовые адреса сегментов. Базовые (32-битовые) адреса определяются путем умножения значений сегментных адресов на 16. Перед обычной инициализацией сегментного регистра DS (предложения 31-32), которая позволит нам обращаться к полям данных программы (в реальном режиме) выполняется очистка регистра EAX (предложение 30). В АХ содержится сегментный адрес сегмента данных. Командой shl (предложение 33) содержимое регистра EAX также сдвигается на 4 бита, что соответствует умножению на 16. После этого в  EAX будет располагаться линейный адрес сегмента данных, совпадающий в нашем случае с физическим.

Следующей командой (предложения 36) содержимое АХ отправляется в поле base_l дескриптора gdt_data. После этого нам необходимо получить старшую часть линейного адреса сегмента (находящуюся в старшей части EAX). Для этого используется команда rol, циклически обращающая биты регистра. При сдвиге на 16 бит влево старшая и младшая (AX) части 32-х битного регистра меняются местами. Таким образом, необходимая нам часть адреса окажется в AL, откуда мы помещаем ее в поле base_m (предложение 38).

Поскольку нам еще понадобится линейный адрес сегмента данных, сохраним его предварительно в регистре EBP (предложение 34).

Аналогично вычисляются 32-битовые адреса сегментов команд и стека, помещаемые в дескрипторы gdt_code и gdt_stack.

Следующий этап подготовки к переходу в защищенный режим – загрузка в регистр процессора GDTR (Global Descriptor Table Register, регистр таблицы глобальных дескрипторов) информации о таблице гло­бальных дескрипторов. Эта информация включает в себя линейный ба­зовый адрес таблицы и ее границу и размещается в 6 байтах поля дан­ных, называемого иногда псевдодескриптором. Для загрузки GDTR существует специальная привилегированная команда lgdt (load global descriptor table, загрузка таблицы глобальных дескрипторов), которая требует указания в качестве операнда имени псевдодескриптора. Формат псевдодескриптора приведен на рисунке 4.3.

 

Рисунок 4.3 – Формат псевдодескриптора.

За­полнение псевдодескрип­тора упрощается вследст­вие того, что таблица гло­бальных дескрипторов расположена в начале сегмента данных, и ее базовый адрес совпадает с базовым адресом всего сегмента, который уже был вычислен и помещен в дескриптор gdt_data. В предложении 53 компоненты базового адреса переносятся из дескриптора в требуемые поля pdescr, а в предложении 54 заполняется поле границы. Команда lgdt загружает регистр GDTR и сообщает процессору о местонахожде­нии и размере GDT.

Мы запускаем программу под управлением DOS, и естественно завершить ее также обычным обра­зом, чтобы не нарушить работоспособность системы. Но в защищенном режиме запрещены любые обращения к функциям DOS или BIOS. Причина этого совершенно очевидна – и DOS, и BIOS являются про­граммами реального режима, в которых широко используется сегмент­ная адресация реального режима, т.е. загрузка в сегментные регистры сегментных адресов. В защищенном же режиме в сегментные регистры загружаются не сегментные адреса, а селекторы. Кроме того, обращение к функциям DOS и BIOS осуществляется с помощью команд про­граммного прерывания int с определенными номерами, а в защищен­ном режиме эти команды приведут к совершенно иным результатам. Таким образом, программу, работающую в защищенном режиме, нельзя завершить средствами DOS. Сначала ее надо вернуть в реальный режим.

Возврат в реальный режим можно осуществить двумя способами.

Первый способ – программный сброс процессора. Действия процессора после сброса определяются одной из ячеек КМОП-микросхемы – байтом состояния отключения, располагаемым по адресу 0Fh. В частности, если в этом байте записан код 0Ah, после сброса управление немедленно передается по адресу, который извлека­ется из двухсловной ячейки 40h:67h, расположенной в области данных BIOS. Таким образом, для подготовки возврата в реальный режим мы должны в ячейку 40h:67h записать адрес возврата, а в байт 0Fh КМОП-микросхемы занести код 0Ah. Точка возврата может располагаться в любом месте программы.

Сброс процессора выполняется засылкой команды FEh в порт 64h контроллера клавиатуры. Эта команда возбуждает сигнал на од­ном из выводов контроллера клавиатуры, который приводит к появлению сигнала сброса на выводе RESET микропроцес­сора. Перед выполнением сброса текущее содержимое SP сохраняется в ячейке rеal_sp, чтобы после перехода в реальный режим можно было восстановить состояние стека.

После сброса процессор начинает работать в реальном режиме, причем управление передается программам BIOS. BIOS анализирует со­держимое байта состояния отключения (0Fh) КМОП - микросхемы и, по­скольку мы записали туда код 0Ah, осуществляет передачу управления по адресу, хранящемуся в ячейке 40h:67h области данных BIOS. В нашем случае переход осуществляется на метку return. Команда hlt (halt, останов) позволяет организовать ожидание сброса процессора, который выполняется не мгновенно. Вместо команды hlt можно было использовать бесконечный цикл. Если команду ожидания опустить, процессор до своего останова успеет выполнить несколько следующих команд, после чего все-таки передаст управление на адрес возврата.

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

Еще одна важная операция, которую необходимо выполнить перед переходом в защищенный режим, заключается в запрете всех аппарат­ных прерываний. В защищенном режиме процессор вы­полняет процедуру прерывания не так, как в реальном. При поступле­нии сигнала прерывания процессор не обращается к таблице векторов прерываний в первом килобайте памяти, как в реальном режиме, а из­влекает адрес программы обработки прерывания из таблицы дескрипто­ров прерываний, построенной схоже с таблицей глобальных дескрипто­ров и располагаемой в программе пользователя (или в операционной системе). В примере 6.1 такой таблицы нет, и на время работы программы прерывания придется запретить. Запрет всех аппаратных прерываний осуществляется командой cli (предложение 56). Однако же­лательно запретить еще и немаскируемые прерывания, которые посту­пают в процессор по отдельной линии (вход NMI микропроцессора) и не управляются битом IF регистра флагов. Немаскируемые прерывания обычно используются для обработки таких катастрофических событий, как сбой питания, ошибка памяти или ошибка четности на магистрали; в реальном режиме для них зарезервирован вектор 02h. Для запрета не­маскируемых прерываний не предусмотрено никаких специальных команд, однако, это можно сделать, установив старший бит в ад­ресном (индексном) порте 70h КМОП-микросхемы.

В предложениях 57-59 из порта 70h читается текущее состояние КМОП-микросхемы, и запре­щает немаскируемые прерывания установкой старшего (7-ого) бита и отправкой значения обратно в порт.

Для перехода могут быть использованы команды smsw (Store Machine Status Word, запись сло­ва состояния машины) и lmsw (Load Machine Status Word, загрузка слова состояния машины) работающие с младшими 16 битами регистра CR0, называемыми словом состояния машины. Однако они практически не используются и существуют для совместимости с процессором 80286. Вместо них всегда удобнее использовать mov cr0,еах (вместо lmsw) и mov еах,cr0 (вместо smsw).

Переход в защищенный режим осуществляется установкой в 1 бита 0 регистра CR0, называемого PE (Protection Enabled). Поскольку остальные биты этого слова нам могут быть не известны, сначала мы читаем в регистр EAX содержимое CR0, затем устанавливаем в нем бит 0 и, наконец, записы­ваем модифицированное значение назад в регистр CR0 процессора. Все после­дующие команды выполняются уже в защищенном режиме.

В предложениях 61…63 осуществляется перевод процессора в защи­щенный режим.

Хотя защищенный режим установлен, однако действия по настрой­ке системы еще не закончены. Действительно, во всех используемых в программе сегментных регистрах хранятся не селекторы дескрипторов сегментов, а базовые сегментные адреса, не имеющие смысла в защи­щенном режиме. Между прочим, отсюда можно сделать вывод, что пос­ле перехода в защищенный режим программа не должна работать, так как в регистре CS пока еще нет селектора сегмента команд, и процессор не может обращаться к этому сегменту. В действительности это не сов­сем так.

В процессоре для каждого из сегментных регистров имеется так на­зываемый теневой регистр дескриптора, который имеет формат де­скриптора. Теневые регистры недоступны программисту; они автома­тически загружаются процессором из таблицы дескрипторов каждый раз, когда процессор загружает соответствующий сегментный регистр. Таким образом, в защищенном режиме программист имеет дело с се­лекторами, т.е. номерами дескрипторов, а процессор – с самими де­скрипторами, хранящимися в теневых регистрах. Именно содержимое теневого регистра (в первую очередь, линейный адрес сегмента) опреде­ляет область памяти, к которой обращается процессор при выполнении конкретной команды.

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

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

Загрузить селекторы в сегментные регистры DS, SS и ES не пред­ставляет труда (предложения 68…73). Но как загрузить селектор в про­граммно недоступный регистр CS? Для этого можно воспользоваться искусственно сконструированной командой дальнего перехода, которая, как известно, приводит к смене содержимого и IP, и CS. Предложения 64…66 демонстрируют эту методику. В реальном режиме мы поместили бы во второе слово адреса в сегментный адрес сегмента команд, в защи­щенном же мы записываем в него селектор этого сегмента (число 16).

Команда дальнего перехода, помимо загрузки в CS селектора, вы­полняет еще одну функцию – она очищает очередь команд в блоке предвыборки команд процессора. Как известно, в современных процес­сорах с целью повышения скорости выполнения программы использу­ется конвейерная обработка команд программы, позволяющая совмес­тить во времени фазы их обработки. Одновременно с выполнением те­кущей (первой) команды осуществляется выборка операндов следующей (второй), дешифрация третьей и выборка из памяти четвертой команды. Таким образом, в момент перехода в защищенный режим уже могут быть расшифрованы несколько следующих команд и выбраны из памя­ти их операнды. Однако эти действия выполнялись, очевидно, по пра­вилам реального, а не защищенного режима, что может привести к на­рушениям в работе программы. Команда перехода очищает очередь предвыборки, заставляя процессор заполнить ее заново уже в защищен­ном режиме.

Следующий фрагмент примера (предложения 72…81) является чисто иллюстративным. В нем инициализируется (по правилам защи­щенного режима) сегментный регистр ES и в видеобуфер экрана выво­дится некоторое количество цветных символов, чем подтверждается правильное функционирование программы в защищенном режиме.

Чтобы не нарушить работоспо­собность DOS, процессор следует вернуть в реальный режим, после чего можно будет завершить программу обычным образом.

Для этого необходимо перевести процессор обратно в реальный режим адресации, сбросив бит PE регистра CR0 (предложения 82…84). После этого необходимо загрузить в сегментный регистр CS корректное значение сегментного адреса сегмента кода, а в регистр IP значение смещения точки возврата (предложения 85…87). В нашем случае переход осуществляется на метку return (предложение 88).

Поскольку сегментные регистры содержат селекторы защищенного режима, их следует инициализировать заново, что и выполняется в предложениях 89…93. Заметим, что сохранение и восстановление указателя стека SP не является обязательным, во всяком случае, в нашем примере, где работа программы до перехода в защищенный режим и после возврата из него протекает независимо. Поэтому после возврата в ре­альный режим можно инициализировать SP заново, выполнив команду mov SP, 256 (в предположении, что стек имеет размер 256 байт).

Для восстановления работоспособности системы следует разрешить маскируемые (предложение 94) и немаскируемые прерывания (предложения 95…97), после чего программа может продолжаться уже в реальной режиме. В рассматриваемом примере для проверки работо­способности системы на экран выводится некоторый текст с помощью функции DOS 09h.

Программа завершается обычным образом функцией DOS 4Ch. Нормальное завершение программы и переход в DOS тоже в какой-то мере свидетельствует о ее правильности.

У рассмотренной программы имеется серьезный недостаток – полное отсутствие средств отладки. Для отладки программ защищенного режима используется механизм прерываний и исключений, в нашей же программе этот механизм не активизирован. Поэтому всякие неполадки при работе в защищенном режиме, которые с помощью указанного ме­ханизма можно было бы обнаружить и проанализировать, в данном случае будут приводить к сбросу процессора. Однако после сброса программа, скорее всего, будет работать правильно: на экран будет вы­ведена запланированная строка и программа завершится с передачей управления DOS. Таким образом, критерием программных ошибок за­щищенного режима может служить правильная в целом работа про­граммы при отсутствии на экране цветных символов, которые должны выводиться в защищенном режиме.

Задание для самостоятельного выполнения

1. Изучить возможности работы в защищённом режиме.

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

2.1. Определение структуры дескриптора.

2.2. Определение таблицы дескрипторов и дескрипторов используемых сегментов.

2.3. Подготовка таблицы дескрипторов к переходу в защищенный режим.

2.4. Подготовка псевдодескриптора и инициализация регистра GDTR

2.5. Запрет аппаратных и немаскируемых прерываний

2.6. Непосредственно переход в защищенный режим

2.7. Подготовка к работе в защищенном режиме и вывод в видеобуфер

2.8. Подготовка к возврату и возврат в реальный режим

2.9. Восстановление операционной среды реального режима

3. Отладить и протестировать полученную программу.

4. Оформить отчёт.

Контрольные вопросы

1. Каков формат дескриптора?

2. Каков формат псевдодескриптора?

3. Как переключиться в защищенный режим?

4. Какова структура таблицы дескрипторов?

5. Какова модель памяти при работе в защищенном режиме?

 


Дата добавления: 2021-12-10; просмотров: 21; Мы поможем в написании вашей работы!

Поделиться с друзьями:




Мы поможем в написании ваших работ!