6.1. Адресация в защищенном режиме

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

биты 15-3: номер дескриптора в таблице

бит 2:        индикатор таблицы 0/1 - использовать GDT/LDT

биты 1-0:   уровень привилегий запроса (RPL)

Уровень привилегий запроса - это число от Q до 3, указывающее степень защи­ты сегмента, для доступа к которому применяется данный селектор. Если програм­ма имеет более высокий уровень привилегий, при использовании этого сегмента привилегии понизятся до RPL. Уровни привилегий и весь механизм защиты в за­щищенном режиме нам пока не потребуются.

GDT и LDT - таблицы глобальных и локальных дескрипторов соответствен­но. Это таблицы 8-байтных структур, называемых дескрипторами сегментов, где и находится начальный адрес сегмента вместе с другой важной информацией:

слово 3 (старшее):

биты 15-8: биты 31-24 базы

бит 7: бит гранулярности (0 - лимит в байтах, 1 - лимит в 4-килобайтных еди­ницах)

бит 6: бит разрядности (0/1 - 16-битный/32-битный сегмент) бит 5: О

бит 4: зарезервировано для операционной системы биты 3-0: биты 19-16 лимита слово 2:

бит 15: бит присутствия сегмента биты 14-13: уровень привилегий дескриптора (DPL) бит 12: тип дескриптора (0 - системный, 1 - обычный) биты тип сегмента

биты 7-0: биты 23-16 базы

слово 1: биты 15-0 базы

слово 0 (младшее): биты 15-0 лимита

Два основных поля структуры, которые нам интересны, - это база и лимит сег­мента. База представляет линейный 32-битный адрес начала сегмента, а лимит -20-битное число, которое равно размеру сегмента в байтах (от 1 байта до 1 мега­байта), если бит гранулярности сброшен в ноль, или в единицах по 4096 байт (от 4 Кб до 4 Гб), если он установлен в 1. Числа отсчитываются от нуля, так что ли­мит 0 соответствует сегменту длиной 1 байт, точно так же, как база 0 соответству­ет первому байту памяти.

Остальные элементы дескриптора выполняют следующие функции:

Бит разрядности: для сегмента кода этот бит указывает на разрядность опе­рандов и адресов по умолчанию. То есть в сегменте с этим битом, установ­ленным в 1, все команды будут интерпретироваться как 32-битные, а пре­фиксы изменения разрядности адреса или операнда будут превращать их в 16-битные, и наоборот. Для сегментов данных бит разрядности управляет тем, какой регистр (SP или ESP) используют команды, работающие с этим сегментом данных как со стеком.

2. Поле DPL определяет уровень привилегий сегмента.

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

4. Бит типа дескриптора - если он равен 1, сегмент является обычным сегмен­том кода или данных. Если этот бит - 0, дескриптор является одним из 16 возможных видов, определяемых полем типа сегмента.

5. Тип сегмента: для системных регистров в этом поле находится число:от 0 до 15, определяющее тип сегментов (LDT, TSS, различные шлюзы), которые рассмотрены в главе 9. Для обычных сегментов кода и данных эти четыре бита выполняют следующие функции:

бит     0 - сегмент данных, 1 - сегмент кода

бит 10: для данных - бит направления роста сегмента

для кода - бит подчинения бит 9: для данных - бит разрешения записи

для кода - бит разрешения чтения бит 8:   бит обращения

6. Бит обращения устанавливается в 1 при загрузке селектора этого сегмента в регистр.

7. Бит разрешения чтения/записи выбирает разрешаемые операции с Сегмен­том - для сегмента кода'это могут быть выполнение или выполнение/чте­ние, а для сегмента данных — чтение или чтение/запись.

8. Бит подчинения указывает, что данный сегмент кода является подчинен­ным. Это значит, что программа с низким уровнем привилегий может пе­редать управление в сегмент кода и текущий уровень не из­менится.

9. Бит направления роста, сегмента обращает смысл лимита сегмента. В сег­ментах с этим битом, сброшенным в ноль, разрешены абсолютно все смеще­ния от 0 до лимита, а если этот бит 1, то допустимы все смещения, кроме от О до лимита. Про такой сегмент говорят, что он растет сверху вниз, taic как если лимит, например, равен допустимы смещения от -100 до 0, а если лимит увеличить, станут допустимыми еще меньшие смещения.

Для обычных задач программирования нам не потребуется все многообразие

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

Чтобы создать flat-память, нам потребуются два дескриптора с нулевой базой и максимальным лимитом - один для кода и один для данных.

Дескриптор кода:

лимит OFFFFFh база OOOOOOOOh тип сегмента OFAh бит присутствия = 1

уровень привилегий = 3 (минимальный) бит типа дескриптора *= 1

тип сегмента: 1010b (сегмент кода, для выполнения/чтения)

t

Интерфейс VCPI

i

бит гранулярности = 1 бит разрядности = 1

db OFFh,  OFFh,  Oh,   Oh,  Oh,  OFAh,  OCFh, Oh

Дескриптор данных: лимит OFFFFFh база OOOOOOOOh бит присутствия = 1

уровень привилегий - 3 (минимальный) бит типа дескриптора = 1

тип сегмента: 0010b (сегмент данных, растет вверх, для чтения/записи) бит гранулярности 1 бит разрядности = 1

db OFFh,  OFFh,  Oh,  Oh,  Oh,  OF2h,  OCFh, Oh

Для того чтобы процессор знал, где искать дескрипторы, операционная систе­ма собирает их в таблицы, которые называются GDT (таблица глобальных деск­рипторов - может быть только одна) и LDT (таблица локальных дескрипторов -по одной на каждую задачу), и загружает их при помощи привилегированных команд процессора. Так как мы пока не собираемся создавать операционные сис­темы, нам нужно только подготовить дескриптор и вызвать соответствующую функцию VCPI или DPMI.

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