
Селектор (англ. selector) — число, хранящееся в сегментном регистре; это 16-битная структура данных, которая является идентификатором сегмента. Селектор указывает не на сам сегмент в памяти, а на его дескриптор, в таблице дескрипторов… Селектор «живёт» в сегментном регистре (CS, DS, ES, FS, GS, SS). В реальном режиме содержимое каждого сегментного регистра представляет собой номер параграфа — 16-байтового участка памяти, выровненного на границу 16 байт.
В защищённом режиме каждый сегментный регистр делится на три части, как показано ниже:
15 3 2 1 0
+--------------------+
| Index | TI | RPL|
+--------------------+
Бит TI в этом случае указывает, какая таблица дескрипторов должна использоваться (нуль соответствует глобальной таблице дескрипторов, единица — локальной таблице дескрипторов). Поле Index является номером (индексом) дескриптора в таблице дескрипторов, который должен использоваться при вычислении линейного адреса. Наконец, поле RPL является запрошенным уровнем привилегий, используемым для контроля прав доступа программы к сегменту. Частным случаем RPL является текущий уровень привилегий — CPL, чьё значение в любой момент времени находится в сегментном регистре CS.
Дескриптор сегмента
Дескриптор сегмента — служебная структура в памяти, которая определяет сегмент. Длина дескриптора равна восьми байтам.
Структура сегментного дескриптора
База (жёлтые поля, 32 бита) — начало сегмента в линейной памяти
Лимит (красные поля, 20 бит) — (размер сегмента в байтах)-1 (База+Лимит = линейный адрес последнего байта)
Права доступа (синие поля, 12 бит) — флаги, определяющие наличие сегмента в памяти, уровень защиты, тип, разрядность + один пользовательский флаг[1]
Байт прав доступа (AR, англ. Access Rights, биты 8-15 на рисунке):
Бит P определяет доступность сегмента (0 — сегмента нет, 1 — есть). При обращении к сегменту со сброшенным битом P происходит исключение #NP, обработчик которого может загрузить/создать сегмент.
Номер привилегий DPL содержит 2-битный номер (0-3), определяющий, к какому уровню (кольцу) защиты относится этот сегмент.
Тип сегмента (биты 8-12 на рисунке). Старший бит (S) определяет сегмент как системный (S=0) или пользовательский (S=1). Значение прочих бит для системных и пользовательских сегментов описано в таблице:
Типы системных сегментов | |||||
№ |
Биты AR |
Описание | |||
3 |
2 |
1 |
0 | ||
0 |
0 |
0 |
0 |
0 |
Зарезервировано |
1 |
0 |
0 |
0 |
1 |
Свободный 16-битный TSS |
2 |
0 |
0 |
1 |
0 |
LDT |
3 |
0 |
0 |
1 |
1 |
Занятый 16-битный TSS |
4 |
0 |
1 |
0 |
0 |
16-битный шлюз вызова |
5 |
0 |
1 |
0 |
1 |
Шлюз задачи |
6 |
0 |
1 |
1 |
0 |
16-битный шлюз прерывания |
7 |
0 |
1 |
1 |
1 |
16-битный шлюз ловушки |
8 |
1 |
0 |
0 |
0 |
Зарезервировано |
9 |
1 |
0 |
0 |
1 |
Свободный 32-битный TSS |
A |
1 |
0 |
1 |
0 |
Зарезервировано |
B |
1 |
0 |
1 |
1 |
Занятый 32-битный TSS |
C |
1 |
1 |
0 |
0 |
32-битный шлюз вызова |
D |
1 |
1 |
0 |
1 |
Зарезервировано |
E |
1 |
1 |
1 |
0 |
32-битный шлюз прерывания |
F |
1 |
1 |
1 |
1 |
32-битный шлюз ловушки |
Типы пользовательских сегментов | ||||
№ |
Биты AR |
Описание | ||
3 |
2 |
1 | ||
0 |
0 |
0 |
0 |
Сегмент данных только для чтения |
2 |
0 |
0 |
1 |
Сегмент данных для чтения/записи |
4 |
0 |
1 |
0 |
Сегмент данных только для чтения, растёт вниз |
6 |
0 |
1 |
1 |
Сегмент данных для чтения/записи, растёт вниз |
8 |
1 |
0 |
0 |
Сегмент кода только для выполнения |
A |
1 |
0 |
1 |
Сегмент кода для выполнения/чтения |
C |
1 |
1 |
0 |
Подчинённый сегмент кода только для выполнения |
E |
1 |
1 |
1 |
Подчинённый сегмент кода для выполнения/чтения |
Младший бит байта AR пользовательских сегментов (A, англ. Accessed, бит 8 на рисунке) можно использовать для сбора статистики о сегменте. При первом же обращении к сегменту (чтение, запись, выполнение) он устанавливается процессором в 1.
Флаг гранулярности G определяет лимит сегмента: при G=0 лимит равен значению соответствующего поля в дескрипторе, а при G=1 лимит равен полю дескриптора, умноженному на (212 = 4096). Таким образом при G=0 максимальный размер сегмента 1 МБайт, а при G=1 4 ГБайт.
Флаг разрядности DB (бит 22 на рисунке) актуален для пользовательских сегментов кода и стека. Определяет разрядность в 16 бит при нулевом и 32 бит при единичном значении.
Зарезервированный флаг (серое поле) должен всегда равняться нулю.
Пользовательский флаг AVL (A, бит 20 на рисунке) отдан операционной системе. Его состояние никак не влияет на работу с сегментом.
Дескриптор шлюза
Дескриптор шлюза — служебная структура данных, служащая для различных переходов. Используется только в защищённом режиме. В реальном режиме некоторым аналогом может служить дальний адрес. Длина дескриптора стандартна и равна восьми байтам.
Структура дескриптора шлюза вызова (курсивом обозначены поля, которых не было в 80286
Смещение (англ. Offset, жёлтые поля) — смещение процедуры в сегменте кода;
Селектор (англ. Selector, оранжевое поле) — селектор сегмента процедуры, на который происходит переход. Может быть сегментом кода или TSS;
Количество параметров (англ. Words count, голубое поле, биты 32-36) — количество слов (16-разрядный стек) или двойных слов (32-разрядный стек), копируемых из стека вызывающей программы в стек вызываемой процедуры. Актуально только при смене уровня привилегий; используется только в шлюзах вызова (Call Gate)
Тип/права доступа (голубые поля, биты 40-47) — права доступа к шлюзу и его тип (см. Дескриптор сегмента — типы системных сегментов).
Селектор и смещение составляют обычный дальний адрес точки входа в процедуру.
При выполнении дальних команд CALL, JMP с указанием селектора шлюза, значение смещения, указываемого в команде игнорируется.
Алгоритм перехода с использованием шлюза:
Если уровень привилегий вызываемого сегмента кода численно меньше CPL, то происходит смена стека: из соответствующих полей TSS загружаются новые значения SS, (E)SP;
Если была смена стека, то в новый стек сохраняется предыдущие значения SS, (E)SP (до вызова);
Если была смена стека, а шлюз является шлюзом вызова, то в новый стек копируются WC слов/двойных слов из стека вызывающей программы;
Если это шлюз прерывания или ловушки, то сохраняется значение (E)Flags;
Сохраняется текущее значение CS, (E)IP;
Из дескриптора шлюза в регистры CS:(E)IP загружается новый дальний адрес, указанный в соответствующих полях дескриптора;
Теперь подробнее:
Шлюз вызова (англ. Call Gate)
Нельзя использовать в IDT.
Характерной особенностью шлюза вызова является наличие поля WC, благодаря которому возможна передача до 25=32 слов/двойных слов вызываемой процедуре через стек. Подробнее...
Шлюз задачи (англ. Task Gate)
Может использоваться в любой из трёх дескрипторных таблиц.
Единственная особенность этого шлюза состоит в том, что в качестве сегмента указан сегмент TSS. Поля смещения не используются и могут иметь любое значение.
Шлюз прерывания (англ. Interrupt Gate)
Этот шлюз используется только в IDT.
Его особенностью является сброс флага прерываний IF при входе в процедуру обработки. Так как прежнее значение регистра флагов сохраняется в стеке, то при выходе из процедуры обработки флаг IF принимает исходное положение. Это позволяет обрабатывать некоторые прерывания в режиме CLI.
Шлюз ловушки (англ. Trap Gate)
Этот шлюз используется только в IDT.
Самый простой шлюз. При поступлении прерывания просто переходит на процедуру обработки (переключение стека, если надо, происходит).
Сегментная защита памяти
Определения
Существует четыре уровня привилегий (PL) сегментов (0–3). Привилегированность увеличивается с уменьшением номера (т. е. нулевой – самый привилегированный). Уровни 0–2 соответствуют супервизору (Supervisor), 3 – пользователю (user). На нулевом уровне позволяется использование привилегированных инструкций.
Уровень привилегий сегмента (DPL англ. Descriptor Privilege Level) соответствует значению поля DPL в дескрипторе сегмента.
Текущий уровень привилегий (CPL англ. Current Privilege Level) соответствует уровню привилегий сегмента кода, селектор которого загружен в регистр CS (т. е. уровню привилегий выполняющегося сегмента кода).
Запрашиваемый уровень привилегий (RPL англ. Requested Privilege Level) находится в двух младших битах селектора (задаётся программой).