Лекции по Вычислительным машинам, системам и сетям   

3. Тридцатидвухразрядные процессоры фирмы Intel

3.6. Сегментный механизм

Рассмотрим более подробно формат сегментного регистра, показанный на рис. 3.9.

15                  3

2

1  0

 

63                         0

Селектор

TI RPL  

Копия дескриптора

Видимая часть

 

Теневая часть

Рис. 3.9

Тринадцать старших разрядов видимой части занимает селектор, адресующий дескриптор в одной из двух дескрипторных таблиц. В какой именно таблице находится этот дескриптор, определяет бит TI. Если он равен 0, то адресуется GDT, при TI = 1 адресуется LDT. В теневой части МП хранит копию этого дескриптора.

Поле RPL задает «запрашиваемый уровень привилегий». Существуют три структуры, связанные с понятием уровня привилегий:

DPL это два бита в дескрипторе, задающие уровень привилегий сегмента;

CPL ‑ это два младших бита видимой части сегмент-ного регистра cs, т.е. это текущий уровень привилегий, на котором выполняется текущая программа (задача);

RPL ‑ это два младших бита слова (селектора), загру-жаемого в видимую часть любого сегментного регистра при смене сегмента.

При смене сегмента данных МП проверяет права доступа к новому сегменту, реализуя проверку неравенства:

DPL³max {CPL, RPL}.

Если это условие не выполняется, то доступ к новому сегменту данных предоставлен не будет, а будет прерывание. При смене сегмента кода проводится более жесткая проверка:

DPL = max {CPL, RPL}.

Из всего сказанного выше можно сделать следующие выводы. Можно перейти к новому сегменту данных, уровень привилегий которого равен нашему CPL или ниже его (по смыслу ниже, а по величине больше), путем непосредственного обращения к дескриптору этого сегмента. Перейти же к более привилегированному (чем текущий CPL) сегменту данных не удастся, поскольку шлюзов для сегментов данных не существует. Для сегментов кода ситуация иная. Можно перейти к сегменту кода с таким же уровнем привилегий (совпадающим с CPL), обратившись к его дескриптору, или перейти к более привилегированному сегменту кода через шлюз. Невозможно перейти к менее привилегированному сегменту кода, даже если он помечен как согласованный. Кроме того, существует правило, согласно которому текущий DPL стека должен равняться CPL. То есть уровень привилегий стека должен быть таким же, как уровень привилегий текущей программы (сегменты данных могут быть менее привилегированными). За соблюдением этого правила следит сам МП, автоматически меняя стек при изменении уровня CPL на более высокий. Как он это делает, будет показано в дальнейшем.

Поле RPL иногда используется для понижения текущего уровня привилегий. Если хотим, чтобы RPL ни на что не влияло, надо брать RPL = 0. Например, RPL может использоваться в следующей ситуации. Пусть имеется привилегированная подпрограмма (нулевого уровня), к которой можем обратиться с пользовательского (третьего) уровня через известный нам шлюз. Пусть эта подпрограмма записывает информацию в заказанную область памяти. Параметром, передаваемым в эту подпрограмму, является селектор дескриптора сегмента данных, в который запрашиваем запись. Поскольку этот селектор формирует программист перед обращением к подпрограмме, то может возникнуть соблазн «обмануть» систему. Для этого записываем в этот селектор RPL = 0 и «просим» подпрограмму записать информацию в какой-нибудь системный сегмент (сегмент данных нулевого уровня).

Если не предпринимать никаких мер, эта попытка будет успешной, так как подпрограмма имеет CPL = 0 и RPL = 0. Процессор отследить такие ситуации не может, они для него слишком сложные. Однако эту ситуацию может отследить сама подпрограмма. Для этого, получив селек-тор, она определяет CPL задачи, которая ее вызвала. Сделать это ей нетрудно, поскольку в ее стеке лежит адрес возврата в исходную программу. Определив, что вызов был с третьего уровня, подпрограмма меняет RPL в переданном ей селекторе с нуля на тройку, понижая тем самым собственный уровень привилегий. В результате мы не сможем обратиться к заказанному сегменту данных, если его уровень привилегий выше трех.

Далее рассмотрим работу сегментного механизма при обращении в текущем сегменте, допустим, на примере выполнения команды mov [bx+2] ,al.

1. МП вычисляет Аэф, для нашей команды Аэф = (bx)+2.

2. МП проверяет, не выводит ли полученный Аэф за заданные границы сегмента. Размер сегмента МП (для рассматриваемой команды) возьмет из теневой части сегментного регистра ds.

3. МП проверяет соответствие выполняемой команды заданным атрибутам сегмента. Атрибуты он также возьмет из теневой части ds. Для рассматриваемой команды будет произведена проверка «разрешен ли сегмент для записи».

4. Если обе проверки прошли успешно, то МП формирует физический адрес по формуле Аф = начальный адрес сегмента + Аэф. Начальный адрес сегмента он также возьмет из теневой части ds.

5. Если какая-то из проверок была неудачной, то МП генерирует соответствующее прерывание.

Рассмотрим далее работу сегментного механизма при смене сегмента. Чтобы перейти к новому сегменту (сменить сегмент), надо загрузить новый селектор в видимую часть соответствующего сегментного регистра. Для регистра cs здесь можно использовать команды jmp far, call far и другие, а для остальных сегментных регистров ‑ mov sr , r16;  pop sr  и ряд других.

МП берет загружаемый селектор и по биту TI определяет, на какую дескрипторную таблицу он ссылается. Затем МП проверяет, не выводит ли этот селектор за заданные (в gdtr или ldtr) размеры таблицы. Если не выводит, МП обращается в таблицу и находит там адресуемый дескриптор. Производится ряд проверок (в частности, проверяются права доступа) и, если все они прошли удачно, в теневую часть сегментного регистра (в который команда загружает селектор) копируется найденный дескриптор, а в видимую часть этого регистра из команды загружается селектор.

Рассмотрим примерную последовательность действий МП при выполнении команды pop ds. Эта команда выталкивает из стека в видимую часть ds новый селектор и, следовательно, должна (если все проверки пройдут успешно) привести к смене сегмента данных. Однако сначала МП еще должен найти в стеке этот селектор. Поэтому вначале выполнения этой команды идет обращение в текущем сегменте стека.

1. МП вычисляет Аэф = (sp).

2 Проверяет, не выводит ли этот адрес за заданные границы стека. Размер стека он берет из теневой части ss.

3. Если проверка прошла удачно, то МП берет из теневой части ss начальный адрес сегмента стека и формирует Аф = начальный адрес + Аэф. По этому адресу из ОП считывается слово, представляющее собой (по смыслу команды) новый селектор. Только с этого момента начинается смена сегмента.

3. По биту TI определяется дескрипторная таблица.

4. Проверяется, не выводит ли селектор за заданные размеры таблицы.

5. В таблице отыскивается адресуемый дескриптор.

6. Проверяется, находится ли сегмент в ОП (по информации в дескрипторе).

7. Проверяются права доступа.

8.  Проверяется, отмечен ли адресуемый дескриптор как дескриптор сегмента данных (поскольку в команде указан ds) или как сегмент кода, разрешенный для чтения.

9. Если все проверки прошли успешно, то в теневую часть ds загружается копия найденного дескриптора и далее мы работаем с новым сегментом данных.

Приведем требования к сегментам, загружаемым в различные сегментные регистры процессора:

- в cs можно загружать только дескрипторы сегментов кода, причем запись в кодовый сегмент всегда запрещена;

- в ds, es, fs и gs можно загружать дескрипторы сегментов данных и дескрипторы сегментов кода, но последние должны обязательно быть разрешены для чтения;

- наиболее жесткими являются требования для регистра ss, в этот регистр можно загружать только дескрипторы сегментов данных, обязательно разрешенные для записи, у которых dpl = cpl и биты G и D в дескрипторе (см. формат дескриптора ) равны между собой.

При любом нарушении вышеперечисленных условий будет прерывание.

Сегментный механизм в защищенном режиме выключить нельзя, но его можно подавить. Для этого надо обеспечить загрузку в теневые части всех сегментных регистров дескрипторов, описывающих один и тот же сегмент. Обычно берется сегмент с начальным адресом 00000000h, имеющий размер 4 Гбайта и уровень привилегий 0 или 3. После этого содержимое сегментных регистров не меняется. Таким образом, все время работаем в одном сегменте, в котором располагаются и программа, и стек, и данные. Так как начальный адрес этого сегмента равен нулю, то адрес, поставленный программистом в команде, будет сразу физическим адресом и при этом адресуемый объем памяти равен 4 Гбайта. Это удобно. Такая бессегментная память называется линейной (или плоской – flat) памятью. Однако такой способ организации памяти имеет и недостатки: теряются механизм защиты и многозадачность.

Страничный механизм при такой организации использовать можно. Появляется механизм защиты на уровне страниц. В минимальном случае при организации линейной памяти достаточно иметь GDT, содержащую два дескриптора (один ‑ кода, а другой ‑ данных).



*****

© 2009-2017 Банк лекций siblec.ru
Лекции для преподавателей и студентов. Формальные, технические, естественные, общественные, гуманитарные, и другие науки.