§ Дескриптор

Прежде чем программа сможет обратиться по какому-либо адресу памяти, она должна определить набор сегментов, через которые она сможет получить доступ к памяти.
Сегмент определяется в виде структуры данных, которая называется дескриптор. Размер дескриптора – 8 байт, все дескрипторы хранятся последовательно в специально отведённой области памяти – глобальной дескрипторной таблице.
Далее приведен формат дескриптора:
Биты    Описание
 0..15  Предел, биты 0..15
16..31  Адрес сегмента, биты 0..15
32..39  Адрес сегмента, биты 16..23
    40  Бит A (Accessed)
41..43  Тип сегмента
    44  Бит S (System)
 45,46  DPL (Descriptor Privilege Level)
    47  Бит P (Present)
48..51  Предел, биты 16..19
    52  Бит U (User)
    53  Бит X (64-bit)
    54  Бит D (Default size)
    55  Бит G (Granularity)
56..63  Адрес сегмента, биты 24..31
Эта структура поясняется так:
63 ... 56 55 54 53 52 51 .. 48 47 46 45 44 43 ... 41 40 39 ... 16 15 ... 0
Адрес биты 31-24 G D X U Предел биты 19-16 P DPL S Тип A Адрес биты 23-0 Предел биты 15-0
Как видите, значения предела и адреса сегмента "разбросаны" по всей структуре дескриптора. Это объясняется тем, что впервые защищённый режим появился в 16-разрядном процессоре 80286 и для совместимости с ним дескриптор не переделывали, а расширили дополнительными полями (биты с 49 по 63), благодаря чему программы, написанные для защищённого режима 286-го процессора работают и на 32-разрядных процессорах.
Практически, в программах формат дескриптора удобнее использовать в следующем виде:
descriptor:
    dw limit_low           ; младшее слово предела
    dw address_low         ; младшее слово адреса
    db address_hi          ; 3-й (из четырёх) байт адреса
    db access_rights       ; права доступа
    db limit_hi_and_flags  ; старшая часть предела и флаги GDXU
    db address_hi          ; 4-й байт адреса
Байт прав доступа имеет следующий формат:
Биты  Описание
   0  Бит A (Accessed)
1..3  Тип сегмента
   4  Бит S (System)
 5,6  Поле DPL
   7  Бит P (Present)
Байт старшей части предела и флагов GDXU имеет формат:
Биты  Описание
0..3  Старшая часть предела (биты 16..19)
   4  Бит U (User)
   5  Бит X (64-bit)
   6  Бит D (Default Size)
   7  Бит G (Granularity)

Элементы дескриптора

  • Адрес сегмента – также называется базовым адресом, – 32-разрядный адрес области памяти, с которой начинается сегмент.
  • Предел сегмента – предельное значение смещения в сегменте; также можно рассматривать предел как размер сегмента минус один элемент размера – байт или страницу, смотря в чём измеряется сегмент.
  • Бит A (Acessed) – бит доступа в сегмент. Этот бит показывает, был ли произведен доступ к сегменту, описываемому этим дескриптором, или нет. Если процессор обращался к сегменту для чтения или записи данных или для выполнения кода, размещённых в нём, то бит A будет установлен (равен 1), иначе – сброшен (0).
С помощью бита A операционная система может определить, использовался ли за последнее время этот сегмент или нет и предпринять какие-либо действия.
Бит A процессором только устанавливается, сбрасывать его должна операционная система.
При создании нового дескриптора подразумевается, что бит A будет равен 0 (т.е. обращений к этому сегменту ещё не было).
  • Тип сегмента – трёхбитовое поле, определяющее тип сегмента (см. таблицу 4-1). Каждый бит типа сегмента имеет следующие значения:
  • старший бит (3-й бит в байте прав доступа): если 0, то это сегмент данных, если 1 – то кода
  • средний бит (2-й бит) – W (write-enable) бит разрешения записи: если 0, то запись запрещена, 1 – разрешена
  • младший бит (1-й бит) – E (expansion-direction) направление расширения сегмента:
  • если 0, то вверх (в сторону старших адресов) – как обычно,
  • если 1, то вниз (в сторону младших адресов) – как в стеке.
Для сегмента кода значения битов W и E интерпретируются несколько иначе (см. таблицу)
Бит # 11 10 9 Тип
Название E W
000 Данные Только чтение
001 Данные Чтение и запись
010 Данные Только чтение, расширяется вниз
011 Данные Чтение и запись, расширяется вниз
100 Код Только выполнение
101 Код Только выполнение
110 Код Только выполнение, согласованный
111 Код Выполнение и считывание, согласованный
Примечания:
  • В защищённом режиме процессор запрещает запись в сегмент кода и, как видно из таблицы, не всегда разрешает даже простое считывание (хотя, запись в сегмент кода, конечно же, можно сделать, но не явно)
  • Согласованный сегмент кода означает, что его можно передавать исполнение из сегмента с меньшим уровнем привилегии.
Биты сегмента:
  • Бит S (System) – определяет системный объект. Если этот бит установлен, то дескриптор определяет сегмент кода или данных, а если сброшен, то системный объект (например, сегмент состояния задачи, локальную дескрипторную таблицу, шлюз).
  • Поле DPL (Descriptor Privilege Level) – Уровень привилегий, который имеет объект, описываемый данным дескриптором. Это двухбитовое поле, в него при создании дескриптора записывают значения от 0 до 3, определяющее уровень привилегий.
Например, Если вам нужно, чтобы процессор выполнял программу на нулевом уровне привилегий, то в DPL дескриптора сегмента кода, где размещается программа, должно быть значение 00B.
  • Бит P (Present) – Присутствие сегмента в памяти. Если этот бит установлен, то сегмент есть в памяти, если сброшен, то его нет. Этот бит применяется при реализации механизма виртуальной памяти – если программе понадобится память, то она сохранит содержимое какого-либо сегмента на диск и сбросит бит P. Если любая программа в дальнейшем обратится к этому сегменту, то процессор сгенерирует исключение неприсутствующего сегмента и запустит обработчик этой ситуации, который должен будет подгрузить содержимое сегмента с диска и установить бит P. После этого управление снова передаётся команде, обратившейся к этому сегменту (производится повторное выполнение команды, вызвавшей сбой) и работа программы будет продолжена. Бит P устанавливается и сбрасывается программами, сам процессор его только считывает.
  • Бит U (User) – Бит пользователя. Этот бит процессор не использует и позволяет программе использовать его в своих целях.
  • Бит X (64-bit) – Определяет 64-х битный тип сегмента
  • Бит D (Default size) – Размер операндов по умолчанию. Если бит сброшен, по процессор использует объект, описываемый данным дескриптором, как 16-разрядный, если бит установлен – то как 32-разрядный. Если ваша программа имеет 32-разрядный код, то он должен размещаться в 32-разрядном сегменте кода (т.е. в дескрипторе такого сегмента бит D должен быть равен 1). В защищённом режиме допускается использование одновременно 16- и 32-разрядных сегментов, но при написании новых программ подразумевается, что все сегменты будут 32-разрядные. Подробнее об этом см. в главе "Смешивание 16- и 32-разрядного кода".
  • Бит G (Granularity) – Гранулярность сегмента, т.е. единицы измерения его размера. Если бит G=0, то сегмент имеет байтную гранулярность, иначе – страничную (одна страница – это 4Кб).
Например, сегмент, имеющий предел, равный 2, при G=0 будет иметь размер в три байта, а при G=1 – 12Кб (3 страницы)
Создавать дескрипторы достаточно легко. Все биты и битовые поля находятся в байте прав доступа access_rights (P, DPL, S, Тип, A ) и в старших четырёх разрядах байта limit_hi_and_flags (G, D, X, U). Дескрипторы по типу отличаются значениями байта прав доступа, по свойствам – битами G и D, остальное – значения базового адреса и предела. Когда мы будем рассматривать пример перехода в защищённый режим, вы увидите один из вариантов конструирования дескрипторов.