Эта глава дает ключевой пример инициализации 80386-го в
32-битном функционировании. Системная конфигурация
устанавливается как простейшая защищенная система, как описано в
главе 8, с 4-гигабайтной областью прямой адресации и
двухуровневой системой защиты.
С минимумом кодов и данных программа, представленная в этой
главе, инициализирует 80386-й для работы как 32-битный
процессор. Таким образом, эта программа разрешает защищенный
режим. Используются 2 привилегированных уровня: наиболее
привилегированный - для кода и данных супервизора, и менее
привилегированный уровень - для кода и данных пользователя.
Программа разрешает доступ ко всей 4-гигабайтной области прямой
адресации.
Программа инициализирует несколько системных регистров
80386-го, чтобы сделать возможным защищенный режим и доступ к
необходимым таблицам дескрипторов GDT и IDT. Хотя и не
существует единой последовательности кодов, которая должна быть
использована для инициализации 80386 в 32-битном режиме работы,
важные пункты должны быть хорошо поняты, и они могут быть
об`единены в некоторую заданную программу инициализации. Эти
пункты перечислены ниже.
1. Программа сконструирована так, чтобы выполняться немедленно
после восстановления процессора. Для простоты эта процедура
должна находиться в EPROM, и она начинает выполняться с
исходного адреса. При перезагрузке 80386-й находится в реальном
режиме, но база кодового сегмента изначально задана FFFF0000h
(64К от вершины 4-гигабайтного физического адресного
пространства, величина, превосходящая нормальное 1-мегабайтное
адресное пространство реального режима. IP задан FFF0h, что
приводит к тому, что начальный адрес вызова будет
FFFF0000h + FFF0h (адрес базы кодового сегмента + значение IP) =
FFFFFFF0h в физической памяти. Заметим, что базовые адреса
сегмента и IP, как они заданы, допускают самозагрузку кода в
EPROM, который помещается на вершину 4-гигабайтной физической
памяти.
2. Первая команда в виде программы - JMP. Т.к. этот пример
сделан так, чтобы постоянно находиться в самозагрузчике EPROM в
вершине физического адресного пространства, эта JMP должна быть
внутрисегментной JMP (код операции Е9h, прямой переход внутрь
кодового сегмента), чтобы избежать перезагрузки адреса
код-сегментной базы.
3. Во время работы программы системные регистры GDTR и IDTR
80386-го загружаются значениями, которые указывают на две
таблицы дескрипторов, рассмотренные в главе 8, GDT и IDT. Образы
структур GDT и IDT также включены в самозагрузчик в EPROM.
Важно: все дескрипторы кодов и данных в EPROM-образе
GDT должны иметь набор битов доступа. Дескриптор TSS должен
иметь набор битов доступа и бит восстановления. Замечание:
образы GDT и IDT могут быть скопированы в RAM, если это
понадобится, позволяя изменять таблицы, также как и системные
запуски. Для мультизадачной системы таблицы должны быть
расположены в RAM так, чтобы 80386 мог как следует
контролировать занятые биты нескольких дескрипторов TSS,
существующих в такой системе.
4. Когда таблицы установлены, как показано выше, программа
разрешает защищенный режим путем загрузки значений в CR0
,который устанавливает PE (возможность защиты)бит (бит,
обеспечивающий защиту).Сейчас защищенный режим установлен, и
это означает, что следующие загрузки селекторов в сегментный
регистр вызовут то, что 80386 будет обращаться к GDT, считывать
дескриптор для сегмента и сохранять информацию дескриптора во
внутреннем дескрипторном КЭШ-регистре. Загрузки селекторов могут
быть выполнены с помощью команды MOV или, для регистра CS, через
интерсегментные команды JMP, CALL, INTn, RET или IRET.
5. Программа продолжает инициализировать КЭШ 80386-го для
каждого сегментного регистра. Программа смещает селектор для
сегмента данных супервизора (селектор = 0018h) в сегментные
регистры SS, DS, ES, FS, и GS. Значение селектора таково, что он
ссылается на дескриптор для сегмента данных уровня
супервизора(уровня 0). Этим действием программа делает доступным
большой сегмент данных супервизора. Как определено дескриптором
сегмента данных в GDT, сегмент данных - 32-битный сегмент, т.к.
бит дескриптора D=1. Дескриптор сегмента данных, который
появляется, как показано на рис.8.3, приводится как
дескриптор CDT 3 на рис.11.2. Этот дескриптор сегмента данных
показывает базовый адрес 00000000h и границу максимального
размера FFFFFFFFh. Сегмент данных супервизора, таким образом,
охватывает всю 4-гигабайтную область прямой адресации. Регис-
тры SS, DS, ES, FS, и GS все описывают тот же сегмент
максимального размера.
6. Остается загрузить один последний сегментный регистр -
регистр CS. Это завершается не командой MOV (MOV в CS -
недопустимый код операции), а командой JMP. Т.к. команда JMP
должна загрузить новое значение селектора в CS, необходимо
выполнить интерсегментную JMP (код операции EAh, прямой
интерсегментный переход). Интерсегментная команда JMP
определяет селектор кодового сегмента супервизора (селектор =
0010h) и смещение внутри этого принимающего сегмента (
смещение = линейный адрес команды, следующей за JMP). Кодовый
сегмент, как описывается дескриптором кодового сегмента в
GDT, есть 32-битный сегмент (D=1). Дескриптор кодового
сегмента, приведенный как дескриптор GDT_2 на рис.11.2,
показывает базовый адрес 00000000h и границу максимального
размера FFFFFFFFh. Сегмент кода супервизора, таким образом,
охватывает всю 4-гигабайтную область прямой адресации.
Интерсегментная команда JMP будет использовать значение
селектора, который обращается к дескриптору для кодового
сегмента уровня супервизора (уровня 0). Она будет
использовать смещение, которое определяет адресат информации
относительно вновь загруженного сегментного кода. Как
описано, база этого кодового сегмента - 00000000h, таким
образом определение смещения n приводит к адресу n адресата
информации внутри 4-гигабайтной области прямой адресации.
7. На адресате информации JMP-команды желательно иметь
команду, которая инициализирует стек супервизора. Перемещение
начального значения в регистр ESP - это все, что требуется.
Т.к. базовый адрес всех сегментов определен как 00000000h,
и границы определены как FFFFFFFFh, эта конфигурация
устанавливается так, что сегментные регистры мало
используются когда процедура инициализации закончена.
4-гигабайтная линейная область делается доступной кодам и
данным.
8. Команда LTR инициализирует регистр задачи, указывающий ей
на TSS для этой (и только этой) задачи. TSS требуется, т.к.
система допускает пользователь/суперпользовательскую
архитектуру. Когда обрабатывающийся код находится на
пользовательском уровне, TSS сохраняет начальный указатель
стека супервизора.
Программа - коды и данные,
используемые для инициализации 80386-го в 32-битном режиме.
Программа реализует все шаги, описанные на страницах .
Необходимые структуры данных - рис.11.2 включает образ GDT с
нулевым дескриптором, 4-сегментных дескриптора.
Последняя необходимая структура данных - образ IDT. Каждый
вход IDT - вентиль, а не селектор, и к нему обращаются по его
позиционному номеру в IDT. Например, команда INTn будет давать
направление через вентиль n в IDT.
Этот код и данные разрешают 80386-му защищенный режим. Т.к.
сегментный дескриптор супервизора и сегментный дескриптор кода
пользователя имеют D=1, 32-битный размер операнда и 32-битный
размер адреса - по умолчанию.
ASSUME CS:INITIAL,DS:TABLEDATA
INITIAL SEGMENT PUBLIC AT 0F00h
ORG FFF0h
ASSUME CS:INITIAL,DS:NOTHING,ES:NOTHING
RES_ADR: JMP BODY
ORG 0D000h
BODY: LGDT GDT_PTR
LIDT IDT_PTR
MOV EAX,CR0
OR EAX,00000001h
MOV CR0,EAX
MOV AX,0018h
MOV SS,AX
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
JMP DEST_0FFSET,DEST_SEL
DEST: MOV ESP,00400000h
LTR SIMPLE_TSS_SEL
YOUR_SUPERVISOR: .....
.....
.....
INITIAL ENDS
Рис.11.1 32-битный защищенный супервизор
INITIAL SEGMENT PUBLIC AT 0F000h
ORG 0BFF0h
GDT_PTR: DW 002Fh
DW C000h
DW 0FFFFh
IDT_PTR: DW 07FFh
DW C030h
DW 0FFFFh
ORG 0C000h
GDT:
GDT_0:
DD 00000000b
DD 00000000b
GDT_1:
DW 0067h
DW 8000h
DB 0FFh
DB 10001011b
DB 00000000b
DB 0FFh
GDT_2:
DW 0FFFFh
DW 0000h
DB 0FFh
DB 10011011b
DB 10001111b
DB 0FFh
GDT_3:
DW 0FFFFh
DW 0000h
DB 0FFh
DB 10010011b
DB 10001111b
DB 0FFh
GDT_4:
DW 0FFFFh
DW 0000h
DB 0FFh
DB 11111011b
DB 10001111b
DB 0FFh
GDT_5:
DW 0FFFFh
DW 0000h
DB 0FFh
DB 11110011b
DB 10001111b
DB 0FFh
GDT_END:
IDT:
IDT_0:
DW XXXXh
DW 0010h
DB 00h
DB 11101111h
DW XXXXh
IDT_32:
DW XXXXh
DW 0010h
DB 00h
DB 11100101h
DW XXXXh
IDT_33:
DW XXXXh
DW 0010h
DB 00h
DB 11100101h
DW XXXXh
IDT_END:
INITIAL ENDS
Рис.11.2
TABLEDATASEGMENT
TSS:
DD 00000000h
DD 00400000h
DW 0000h
DW 0018h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DD 00000000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0000h
DW 0FFFFh
TSS_END:
TABLEDATAENDS
Рис.11.3
Когда код супервизора используется для того,
чтобы диспетчеризовать пользовательскую программу (т.е. начать
выполнение программы пользовательского уровня), контроль
должен быть перенесен командой RET или IRET, почти также, как
обслуживающая процедура завершается командой IRET. Однако,
т.к. код пользователя фактически вызывается с уровня
супервизора, код супервизора должен создавать кадр стека на
этот стек.
Требования кадра стека перед выполнением RET или IRET
изображены на рис.11.4. Заметим, что показанный кадр стека
должен быть только кадром на стек супервизора, когда RET или
IRET выполняются. Это требование позволяет установить стек
супервизора, используя значения SS и ESP, содержащиеся в TSS.
То, о чем особенно нужно упомянуть, возникает как раз
перед диспетчеризацией супервизором кода пользователя.
Необходимо, чтобы супервизор перемещал селектор для сегмента
данных пользователя в каждый сегментный регистр DS, ES, FS, и
GS. Значение селектора таково, что он обращается к
дескриптору для сегмента данных пользовательского уровня
(уровня 3). Этим действием программа делает доступным большой
сегмент данных пользователя. Как описывается дескриптором
сегмента данных в GDT, сегмент данных - 32-битный сегмент,
т.к. бит D дескриптора равен 1. Дескриптор сегмента данных
появляется как показано на рис.8.3 и приводится как
дескриптор GDT_5 на рис.11.2. Этот дескриптор сегмента данных
указывает адрес 00000000h и предел максимального размера
FFFFFFFFh. Сегмент данных пользователя, таким образом,
охватывает всю 4-гигабайтную область прямой адресации.
Регистры SS, DS, ES, FS и GS все описывают один и тот же
сугмент максимального размера.
Регистр SS загружается с селектором в сегмент данных
пользователя командами RET и IRET, используемыми для
диспетчеризации программы пользователя. RET или IRET выделяют
переходы привилегированного уровня, т.к. они выталкивают из
кадра стека значение CS адресата информации. Адресат
информации CPL определяется в низших двух битах CS селектора.
При чтении значения адресата информации CPL, отличного от
настоящего значения CPL, 80386-й отмечает, что предполагается
переход с уровня на уровень. Т.к. для адресата информации
привилегированного уровня должен быть использован отдельный
стек, 80386, таким образом, продолжает работу до выталкивания
из стека информации, а именно значения ESP адресата
информации и селектора SS адресата информации.
Принимая во внимание вышеизложенное, пример кода супервизора,
предназначенный для диспетчеризации пользовательской программы,
показан на рис.11.5. Пример создает кадр стека супервизора,
загружает DS, ES, FS и GS c селектором в сегмент данных
пользователя и выполняет команду IRET чтобы произвести
диспетчеризацию. Заметим, что предположение об этом подходе
позволяет коду пользователя полностью не касаться регистров
сегмента; сегмент данных пользователя доступен, начиная с момента,
когда код пользователя начинает выполнение. Возможно, конечно,
позволить коду пользователя загружать селектор сегмента данных,
но это грубый подход, т.к. он требует, чтобы код пользователя
"знал" необходимое точное значение селектора.
Когда код пользователя последовательно вызывает программу в
коде супервизора, программа супервизора должна протолкнуть по
стеку значение сегментного регистра, который будет
использоваться, и, соответственно, в конце обслуживающей
программы вытолкнуть начальные значения селектора
пользователя обратно в сегментные регистры перед выполнением
команды IRET на пользовательском уровне.
PUSHF
SUB ESP,16
MOV [ESP+12],USER_STK_SEL
MOV [ESP+8], USER_STK_PTR
MOV [ESP+4], USER_CODE_SEL
MOV [ESP], USER_INSTR_PTR
MOV ESP,EBP
MOV AX,USER_DATA_SEL
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
IRET
Рис.11.5
Содержание
Вперед