WYOS - Выпуск №4
Защищенный режим процессора
commrade, Вторник, 29 Июнь 2004, 23:42
<p>Приветствую вас, о светлейшие из светлейших, о
наумнейшие из суперумных!</p>
<p>Меня зовут commrade. Со мной можно связаться по email: <a href="mailto:sergo92@yandex.ru">sergo92@yandex.ru</a>, либо по ICQ: 248544753. В этом выпуске я вам расскажу о защищенном режиме работы процессора. О режиме, в котором работает
UNIX, XENIX, OS/2, PC-MOS, QNX, Desk View, сетевая операционная система NOVELL,
операционная система Microsoft WINDOWS, драйверы расширенной памяти EMM386 и
QEMM и СУБД ORACLE. </p>
ВведениеЗащищенный режим работы процессора это режим, в котором реализуются такие возможности современных процессоров, как: многозадачность изолированностьодного приложения от другого, работа с большим объемом оперативной памяти, использование виртуальной памяти, использование на полную катушку всех возможностей "".
Основные отличия реального режима от защищенного<p>В основном эти два режима между собой отличаются, в следующем:</p>
<ol>
<li>Адресация памяти.
<p>В реальном режиме работы процессора память адресуется посредством
двухкомпонентноо логического адреса (пример: <tt>A700:FFFFh</tt>). Делается это потому что для представления
физического адреса в компьютерах IBM PC и IBM XT используется двадцать двоичных
или пять шестнадцатеричнх разрядов. Но все регистры процессора i8086 являются
16-разрядными. Для разрешения этой проблемы используется двухкомпонентны
логический адрес. Логический адрес состоит из 16-разрядных компонент:
компоненты сегмента памяти и компоненты смещения внутри сегмента. Такой способ
адресации накладывает ограничение на адресное пространство до 1 мегабайта плюс
примерно 64 килобайта старшей области памяти для процессоров i80286, i80386 и
i80486.</p>
<p>При работе в защищенном режиме используется более сложный способ адресации памяти, который позволяет избежать ограничения в 1 мегабайт
памяти. Для получения физического адреса памяти используется так называемый
селектор, который фактически является индексом к таблице дескрипторов, где
храняться 24-разрядные базовые адреса, а физический адрес получается сложением
этого базового адреса и смещения.</p>
</li>
<li>Защита
<p>В реальном режиме работы процессора есть свободный доступ для любых программ
к любым областям данных, что представляет потенциальную опасность для
целостности операционной системы. В защищенном режиме работы программа имеют
доступ только к той области данных которую ей выделяет
процессор. Грубо говоря любой процесс (программа)
видит только свою <tt>LDT</tt>. Также для защиты памяти и файлов операционной системы
используется поле <tt>RPL</tt> в селекторе.</p>
</li>
<li>Виртуальная память
<p>Основная идея виртуальной памяти заключается в том, чтобы хранить (и
обновлять) содержимое большой виртуальной памяти на диске, подкачивая отдельные
участки виртуальной памяти в реальную оперативную память по необходимости.
Данная возможность доступна только в защищенном режиме работы </p>
</li>
</ol>
Локальная и глобальная таблицы дескрипторовСелектор - индекс к таблице дескрипторов, где храняться 24-разрядные
базовые адреса для вычисления физического адреса сегмента.<p><img border=0 src='files/images/articles/wyos/selector.png'></p>
<p>Формат селектора следующий (см. рисунок): с 3 бита по 15 отводится под сам
индекс, бит 2 это так называемый индикатор таблицы (Table Indicator далее <tt>TI</tt>),
если этот бит равен нулю, для преобразования адреса
используется так называемая глобальная таблица дескрипторов (Global Descriptor
Table далее <tt>GDT</tt>), в противном случае - локальная таблица дескрипторов (Local
Descriptor Table далее <tt>LDT</tt>). Биты с 0 по 1 задают запрошенный
программой уровень привилегий (Requested Privilege Level далее <tt>RPL</tt>). Поле <tt>TI</tt>
(Table Indicator) состоит из одного бита. Если этот бит
равен нулю, для преобразования адреса используется так называемая глобальная
таблица дескрипторов <tt>GDT</tt> (Global Descriptor Table), в противном случае -
локальная таблица дескрипторов <tt>LDT</tt> (Local Descriptor Table).</p>
<b>Таблица дескрипторов - это просто таблица преобразования адресов,
содержащая базовые 24-разрядные физические адреса сегментов и некоторую другую
информацию. То есть каждый элемент таблицы дескрипторов (дескриптор)
содержит 24-разрядный базовый адрес сегмента и другую информацию, описывающую
сегмент.</b>
<p>Таблица <tt>GDT</tt> - единственная в системе. Обычно в ней находятся описания
сегментов операционной системы. Таблиц <tt>LDT</tt> может быть много. Эти таблицы
содержат описания сегментов программ, работающих под управлением операционной
системы, т.е. отдельных задач. В отдельный момент времени процессор может
использовать только одну таблицу <tt>LDT</tt>. Процессор имеет два регистра,
предназначенныхдля адресации используемых в настоящий момент таблиц <tt>GDT</tt> и <tt>LDT</tt>.
Регистр <tt>GDTR</tt> описывает расположение и размер таблицы <tt>GDT</tt>, а регистр <tt>LDTR</tt>
содержит ссылку на использующуюся в настоящее время таблицу <tt>LDT</tt>.
Регистры процессора <tt>GDTR</tt> и <tt>LDTR</tt> определяют расположение в памяти таблиц <tt>GDT</tt> и
<tt>LDT</tt> соответственно. Формат регистра <tt>GDTR</tt> показан на рисунке:</p>
<p><img border=0 src='files/images/articles/wyos/gdtr.png'></p>
<p>Таблицы <tt>GDT</tt> и <tt>LDT</tt> содержат дескрипторы, описывающие сегменты памяти. В этих
дескрипторах, помимо другой информации содержится 24-разрядный базовый адрес
сегмента. Старшие 13 битов селектора выбирают элемент из таблицы <tt>GDT</tt> или <tt>LDT</tt> в
зависимости от состояния бита <tt>TI</tt> селектора. Извлечённый из таблицы дескрипторов
базовый адрес сегмента складывается процессором для получения 24-разрядного
физического адреса. Структура дескриптора следующая (см. рисунок):</p>
<p><img border=0 src='files/images/articles/wyos/descript.png'></p>
<p>Назначения этих полей следующие:</p>
<p>Базовый адрес – 24-разрядное чило указывающие на физический адрес сегмента.</p>
<p>Резерв – дополнительное 16-разрядное число, совместно с базовым адресом
указывающие на физический адрес сегмента, в основном используется
процессорами i80386, i80486 и выше, т.к. разрядность шины адреса у этих
процессоров 32-бита (<i>Khimov Roman:</i> начиная с Pentium II шина адреса Intel'овских процеесоров - 36 бит (причем, заметьте - в Celeron от Pentium II адресная шина была по прежнему 32-битная!). AMD не отстает...
.</p>
<p>Доступ – 8-разрядное число говорящее о том, к каком типу данных принадлежит тот или иной
сегмент, возможно ли чтение или запись данных в этот сегмент.</p>
<p>Предел – указывает на размер сегмента в байтах, уменьшенный на единицу.</p>
<p>Если с базовым адресом, резервом, и пределом все понятно, то полем доступа
не все так просто. В защищенном режиме существует три типа сегмента. Сегмент
кода, сегмент данных и системный сегмент. Структура этих сегментов покзана на
рисунке ниже:</p>
<p><img border=0 src='files/images/articles/wyos/seg.png'></p>
<p>Остановимся на назначении битов в поле доступа для каждого типа сегмента.
Поле доступа дескриптора сегментов кода содержит битовое поле <tt>R</tt>, называемое
битом разрешения чтения сегмента. Если этот бит
установлен в 1, программа может считывать содержимое сегмента кода. В противном
случае процессор может только выполнять этот код. Поле <tt>W</tt>, поле доступа сегмента
данных, называется битом разрешения записи в сегмент. Бит <tt>C</tt> называется битом
подчинения. Бит <tt>P</tt> называется битом присутствия сегмента в памяти. Для тех
сегментов, которые находятся в физической памяти бит должен быть установлен в
1. Бит <tt>A</tt> называется битом обращения к сегменту. Поле
<tt>D</tt> задаёт направление расширения сегмента. Обычный сегмент данных расширяется в
область старших адресов. Если же в сегменте расположен стек, расширение
происходит в обратном направлении - в область младших адресов. Для сегментов, в
которых организуются стеки, необходимо устанавливать поле <tt>D</tt> равным 1.
Поле <tt>DPL</tt>, используюется для организации защиты сегментов. Данное поле
очень тесно связано полем <tt>RPL</tt> селектора. Дескрипторы
системных сегментов содержат поле <tt>TYPE</tt>, определяющее тип системного сегмента. В
таблице приведены возможные для этого поля значения.</p>
<table border=1 cellpadding=0>
<tr>
<td>
Поле <tt>TYPE</tt>
</td>
<td>
Тип сегмента
</td>
</tr>
<tr>
<td>
0
</td>
<td>
Запрещённое значение
</td>
</tr>
<tr>
<td>
1
</td>
<td>
Доступный <tt>TSS</tt> для процессора i80286
</td>
</tr>
<tr>
<td>
2
</td>
<td>
Сегмент <tt>LDT</tt>
</td>
</tr>
<tr>
<td>
3
</td>
<td>
Занятый <tt>TSS</tt> для процессора i80286
</td>
</tr>
<tr>
<td>
4
</td>
<td>
Вентиль вызова для процессора i80286
</td>
</tr>
<tr>
<td>
5
</td>
<td>
Вентиль задачи для процессоров i80286 и i80386
</td>
</tr>
<tr>
<td>
6
</td>
<td>
Вентиль прерывания для процессора i80286
</td>
</tr>
<tr>
<td>
7
</td>
<td>
Вентиль исключения для процессора i80286
</td>
</tr>
<tr>
<td>
8
</td>
<td>
Запрещённое значение
</td>
</tr>
<tr>
<td>
9
</td>
<td>
Доступный <tt>TSS</tt> для процессора i80386
</td>
</tr>
<tr>
<td>
A
</td>
<td>
Зарезервировано
</td>
</tr>
<tr>
<td>
B
</td>
<td>
Занятый <tt>TSS</tt> для процессора i80386
</td>
</tr>
<tr>
<td>
C
</td>
<td>
Вентиль вызова для процессора i80386
</td>
</tr>
<tr>
<td>
D
</td>
<td>
Зарезервировано
</td>
</tr>
<tr>
<td>
E
</td>
<td>
Вентиль прерывания для процессора i80386
</td>
</tr>
<tr>
<td>
F
</td>
<td>
Вентиль ловушки для процессора i80386
</td>
</tr>
</table>
<p>Программы
реального режима работают всегда с сегментами размером 64 килобайта. Если
программа состоит из нескольких сегментов, то некоторые сегменты могут
перекрываться. Существует потенциальная опасность, что в результате программной
ошибки (например, выхода индекса массива за допустимые пределы) произойдёт
запись в другой сегмент. Процессор i80286 позволяет создавать сегменты любого
размера в пределах 64 килобайт (процессоры i80386 и i80486 могут работать с
сегментами размером 4 гигабайта). Кроме того, он следит за тем, чтобы при
адресации памяти не происходил выход за границы сегмента. Границы сегмента
задаются полем предела в дескрипторе сегмента. Мы уже говорили, что значение
этого поля должно быть равно размеру сегмента в байтах минус единица.
Интерпретация поля предела зависит от состояния бита <tt>D</tt> поля доступа. Для
сегментов стеков необходимо устанавливать поле <tt>D</tt> равным 1.
В этом случае попытка записи в переполненный стек вызовет прерывание программы.
В ответ на это прерывание операционная система может, например, выделить
дополнительную память для стека. В реальном режиме переполнение стека никак не
контролируется и может привести к разрушению самой программы или операционной
системы.</p>
Команды защищенного режима<p>Все
описанные выше структуры данных легко можно описать и задать с помощью
ассемблера для этих целей подойдет как TASM (как наиболее популярный интерпретатор) так и FASM (как наиболее простой в
использовании). У этих двух ассемблерных интепретаторов имеются команды для
работы в защищенном режиме и выхода в защищенный режим. Вот основные команды
ассемблера для выхода и работы в защищенном режиме (более детально данные
команды нужно рассматривать исходя из конкретного
интепретатора):</p>
<table border=1 cellpadding=0>
<tr>
<td>
Команда
</td>
<td>
Выполняемые функции
</td>
</tr>
<tr>
<td>
<tt>LGDT</tt>
</td>
<td>
Загрузка регистра глобальной таблицы дескрипторов <tt>GDTR</tt>
</td>
</tr>
<tr>
<td>
<tt>LLDT</tt>
</td>
<td>
Загрузка регистра локальной таблицы дескрипторов <tt>LDTR</tt>
</td>
</tr>
<tr>
<td>
<tt>LIDT</tt>
</td>
<td>
Загрузка регистра таблицы дескрипторов прерываний <tt>IDTR</tt>
</td>
</tr>
<tr>
<td>
<tt>LTR</tt>
</td>
<td>
Загрузка регистра задачи <tt>TR</tt>
</td>
</tr>
<tr>
<td>
<tt>LMSW</tt>
</td>
<td>
Загрузка слова состояния машины <tt>MSW</tt>
</td>
</tr>
<tr>
<td>
<tt>CLTS</tt>
</td>
<td>
Сброс флага переключения задачи
</td>
</tr>
<tr>
<td>
<tt>HLT</tt>
</td>
<td>
Останов процессора
</td>
</tr>
</table>
Выход в защищенный режим<p>Перед тем, как переключить процессор в защищённый режим, надо выполнить
некоторые подготовительны действия, а именно:</p>
<ol>
<li>Подготовить в оперативной памяти глобальную таблицу дескрипторов <tt>GDT</tt>. В
этой таблице должны быть созданы дескрипторы для всех сегментов, которые будут
нужны программе сразу после того, как она переключится в защищённый режим.
Впоследствии, находясь в защищённом режиме, программа может модифицировать <tt>GDT</tt>
(если, разумеется, она работает в нулевом кольце защиты). Программа может
модифицировать имеющиеся дескрипторы или добавить новые, загрузив заново
регистр <tt>GDTR</tt>.</li>
<li>Для обеспечения возможности возврата из защищённого режима в реальный
необходимо записать адрес возврата в реальный режим в область данных BIOS по адресу
<tt>0040h:0067h</tt>, а также записать в CMOS-память в ячейку <tt>0Fh</tt> код <tt>5</tt>. Этот код
обеспечит после выполнения сброса процессора передачу управления по адресу,
подготовленномунами в области данных BIOS по адресу <tt>0040h:0067h</tt>.</li>
<li>Запретить все маскируемые и немаскируемые прерывания.</li>
<li>Открыть адресную линию <tt>A20</tt>.</li>
<li>Запомнить в оперативной памяти содержимое сегментных регистров, которые
необходимо сохранить для возврата в реальный режим, в частности, указатель
стека реального режима.</li>
<li>Загрузить регистр <tt>GDTR</tt>. </li>
</ol>
<p>В терминах языка ассемблера структура дескриптора может
быть описана следующим образом (здесь и далее рассматриваеются тексты на языке
ассемблер для TASM 5.0 взятые из статьи [1]:</p>
STRUC desc_struc
limit dw 0
; предел сегмента
base_lo dw 0
; младшее слово 24-битового физического адреса сегмента
base_hi db 0
; старший байт 24-битового физического адреса сегмента
access db 0
; поле доступа
reserved dw 0
; зарезервировано для сегментов процессора i80286 должно быть равно нулю
ENDS desc_struc
<p>Тогда мы можем определить таблицу <tt>GDT</tt> как набор дескрипторов со структурой
<tt>desc_struc</tt>:</p>
GDT_BEG = $ ; отмечаем начало GDT
LABEL gdtadr WORD
gdt_0 desc_struc <0,0,0,0,0> ;первый элемент не используется
gdt_gdt desc_struc
gdt_ds desc_struc
gdt_cs desc_struc
gdt_ss desc_struc
GDT_SIZE = ($ - GDT_BEG) ; вычисляем размер GDT
<p>Нам необходимо также замаскировать все маскируемые и
немаскируемые прерывания:</p>
cli
mov al,8f
out CMOS_PORT,al
jmp next1 ; небольшая задержка
next1:
mov al,5
out CMOS_PORT+1,al
<p>Открытие адресной линии <tt>A20</tt> - необходимо в том случае, если
ваша программа будет обращаться к оперативной памяти, лежащей за пределами
первого мегабайта:</p>
;---------------------------------------
; Процедура открывает адресную линию A20
;---------------------------------------
PROC enable_a20 NEAR
mov al,A20_PORT
out STATUS_PORT,al
mov al,A20_ON
out KBD_PORT_A,al
ret
ENDP enable_a20
<p>Эта магическая последовательноть команд выдаёт команду <tt>A20_ON</tt> клавиатурному
процессору 8042, к которому подключены схемы управления адресной линией <tt>A20</tt>. После начального сброса
линия <tt>A20</tt> закрыта, и расширенная память за границами первого мегабайта
недоступна. </p>
<p>Следующий этап - запоминание содержимого сегментных
регистров, которые будут нужны при возврате в реальный режим. Это сегментные
регистры <tt>SS</tt> и <tt>ES</tt>:</p>
mov [real_ss],ss ; запоминаем указатель стека
mov [real_es],es ; для реального режима
<p>На последнем перед переключением в защищённый режим этапе мы
загружаем регистр <tt>GDTR</tt> адресом подготовленной заранее
<tt>GDT</tt>:</p>
lgdt [QWORD gdt_gdt]
<p>Всё! Можно переключаться в защищённый режим! </p>
Послесловие<p>Данная статья была в основном написана под впечатлением от книги
"Защищенный режим процессоров Intel 80286/80386/80486" Александр
Фролов, Григорий Фролов. Приведенные здесь примеры кода нетрудно переписать как
под <a href='http://fasm.sourceforge.net'>FASM</a> так и под <a href='http://www.cryogen.com/Nasm'>Nasm</a>. Если вы закачеете файл по ссылке, то вместе с html-вариантом
книги Александра Фроловa вы получите архив с примерами программ для работы в
защищенном режиме. </p>
<p>Вот теперь все. Всех благ! С вами был commrade. </p>
Список использованной при подготовке
материала литературы и ссылки в интернет:<ol>
<li>
<a href='http://stratum11.pstu.ac.ru/~leonid/base/fv06.zip'>"Защищенный режим процессоров Intel 80286/80386/80486" Александр Фролов, Григорий </a>
</li>
<li>
<a href='http://www.citforum.ru/hardware/'>http://www.citforum.ru/hardware/</a> - море информации по различным
апаратным платформам
</li>
<li>
<a href='http://www.nondot.org/sabre/os/articles'>http://www.nondot.org/sabre/os/articles</a> - информация по различным аспектам написания ОС, правда все на английском
</li>
<li>
<a href='http://lib.prm.ru/'>http://lib.prm.ru/</a> - электронная библиотека
</li>
</ol>
Послесловие от Khimov Roman<p>
Очередной выпуск. Надеюсь, вы начали читать его не отсюда и уже прочитали
<br>
Теперь немного о грустном: некоторое (и довольно длительное) время рассылка будет существовать без основателя, то есть
Wanderer'a. О причинах мы и сами многого не знаем, но ничего не попишешь. В принципе, это не повод расстраиваться,
мы приложим максимум усилий, чтобы рассылка по прежнему выходила раз в неделю.<br>
На время отсутствия Wanderer'а "главным " рассылки остаюсь я, Химов Роман (ну надо же! - по-русски написал!
. Так как на этой должности я только осваиваюсь, то вчера вы получили письмо с темой "Test", я проверял как работает рассылка
. Дайджест-режим я выключил для всех - так выпуски будут доходить быстрее и менее вероятны глюки
mailman'a (этот товарищ не очень любит дайджесты...
. Вопросы, связанные с этим выпуском направляйте выпускающему, то есть <a href='mailto:sergo92@yandex.ru'>commrade</a>. Общие вопросы направляйте мне, на <a href='mailto:khimov@osrc.info?subject=WriteYourOwnOS'>khimov@osrc.info</a>.
</p>
<p>
Следующий выпуск будет посвящен файловым системам, тема обширная, возможно растянем на два выпуска, чтобы поподробнее. Готовить выпуск будет Doberman. Не пропустите!
</p>
это контент от Центр информации по операционным системам
( http://www.osrc.info/plugins/content/content.php?content.8 )