Типы системных дескрипторов
TYPE |
Тип системного дескриптора |
0000 |
Резервировано |
0001 |
TSS, 16-разрядный (доступен) |
0010 |
LDT |
0011 |
TSS, 16-разрядный (занят) |
0100 |
Шлюз вызова подпрограммы, 16-разрядный |
0101 |
Шлюз вызова задачи |
0110 |
Шлюз вызова прерываний, 16-разрядный |
0111 |
Шлюз вызова ловушки, 16-разрядный |
1000 |
Резервировано |
1001 |
TSS, 32-разрядный (доступен) |
1010 |
Резервировано |
1011 |
TSS, 32-разрядный (занят) |
1100 |
Шлюз вызова подпрограммы, 32-разрядный |
1101 |
Резервировано |
1110 |
Шлюз вызова прерываний, 32-разрядный |
1111 |
Шлюз вызова ловушки, 32-разрядный |
Дескриптор таблицы LDT обеспечивает обращение к локальной таблице дескрипторов для выбора сегментов, используемых при выполнении текущей программы.
Дескрипторы сегментов TSS используются при переключении задач в многозадачном режиме. При этом бит 3 поля TYPE байта доступа указывает, решается ли вызываемая задача с использованием 16- или 32-разрядных адресов и данных. Бит B в байте доступа сегмента TSS называется битом занятости и указывает, является ли вызываемый сегмент занятым (соответствующая задача находится в процессе выполнения) или доступным (задача еще не поступила на выполнение). Переключение на занятую задачу вызывает исключение типа #GP (“нарушение защиты”). Отметим, что дескрипторы LDT и TSS всегда должны иметь значение бита разрядности D/B=0.
Дескрипторы шлюзов (gate descriptors) используются для реализации специальных правил доступа при обращении к программам и задачам, а также при обработке прерываний и ловушек.
Дескрипторы сегментов, использующих 16-разрядные адреса и операнды при размере сегментов до 1М-байт (значения битов G = D/B = 0), имеют нулевые значения байтов 5 и 6 дескриптора (рис.$4). Такие дескрипторы использовались в 16-разрядных микропроцессорах 80286. Остальные биты и поля дескрипторов для процессоров 80286 полностью соответствуют форматам, приведенным на рис.$4, $5. Поэтому процессоры семейства IA-32, обеспечивают полную программную совместимость с 16-разрядным микропроцессором 80286, выполняя сегменты инструкций (программы), написанные для 80286, без какой-либо модификации. Отличие состоит только в разрядности базового адреса, которая для 80286 равна 24 битам (ВА[23:0] в байтах 2, 3, 4 дескриптора), и границе сегмента, которая не должна превышать 64KB (L[15:0] в байтах 0,1 дескриптора).
Привилегии и защита памяти
В архитектуре IA-32, для защиты информации, хранящейся в сегментах памяти, используется система привилегий, которая регулирует доступ к тому или иному сегменту в зависимости от уровня его защищенности и от степени важности (привилегированности) запроса. Здесь установлены четыре уровня привилегий PL (privilege level), которые задаются номерами от 0 до 3 (иногда их называют кольцами защиты). Наиболее привилегированным является уровень с меньшим номером. Степень защищенности сегмента также имеет четыре уровня. Наименее защищенными (привилегированными) являются прикладные программы пользователя, для которых выделяется уровень с номером PL=3. Уровни с номерами PL=0, 1, 2 отводятся для системных программ (супервизора), которые можно разделить на три уровня в зависимости от требований к их защищенности. Наиболее защищенная часть – ядро операционной системы (ОС) имеет уровень PL=0. В ядро входит часть ОС, обеспечивающая инициализацию работы, управление доступом к памяти, защиту и ряд других жизненно важных функций, нарушение которых полностью выводит систему из строя. Основная часть программ ОС (утилиты) должна иметь уровень PL=1. К уровню PL=2 обычно относят ряд служебных программ ОС, например, драйверы внешних устройств, системы управления базами данных, специализированные подсистемы программирования и другие.
Для программ с различными уровнями привилегий организованы отдельные стеки. Таким образом, в системе, использующей все четыре уровня привилегий, функционируют четыре различных стека, каждый из которых обслуживает только программы соответствующего уровня. При передаче управления программе другого уровня производится переключение стеков.
В соответствии с уровнями привилегий и защищенности установлены следующие правила доступа для сегментов программ и данных.
Данные из сегмента, имеющего уровень защиты PL, могут быть выбраны программой, имеющей такой же или более высокий уровень привилегий.
Сегмент программ (процедура), имеющий уровень защиты PL, может быть вызван программой, имеющей такой же или более низкий уровень привилегий. Различные варианты вызова сегментов программ описаны ниже.
Уровни защиты и привилегий определяются двумя битами, значение которых указывает номер уровня защиты. В зависимости от места размещения эти биты имеют различное назначение.
Уровень привилегий дескриптора DPL (Descriptor PL) задается битами 5,6 в байте доступа дескриптора (см. рис.$5). Указывает уровень защищенности сегмента, т.е. номер кольца защиты, к которому он относится.
Уровень привилегий запроса RPL (Requested PL) задается битами 0,1 селектора, загруженного в сегментный регистр при обращении к соответствующему сегменту. Этот уровень устанавливается выполняемой программой, которая с помощью данного селектора обращается к памяти системы.
Текущий уровень привилегий CPL (Current PL) задается битами 0,1 селектора, размещенного в регистре сегмента программ CS. Определяет уровень привилегий выполняемого в настоящий момент сегмента программы.
Напомним, что меньшее значение DPL, RPL, CPL соответствует более высокому уровню привилегий (защиты). Наиболее защищенный сегмент имеет значение DPL=0, наименее защищенный – DPL=3. Для наименее привилегированных программ пользователя CPL=3, наиболее привилегированные программы ядра ОС имеют CPL=0.
Процессор имеет специальную инструкцию ARPL, используемую для коррекции значения RPL селектора. Данная инструкция выполняет сравнение RPL селектора и поля RPL в указанном регистре или ячейке памяти. Результатом инструкции является селектор, в котором эти биты, имеют максимальное из значений, заданных в исходных полях RPL.
Обращение к сегментам программ и данных в защищенном режиме производится с учетом описанной выше системы привилегий. При этом правила обращения зависят от типа сегмента.
Обращение к сегментам данных производится с помощью селекторов, загружаемых в регистры DS, ES, FS, GS. При обращении анализируются значения RPL селектора и CPL текущей программы. Эффективный уровень привилегий для запроса данных в этом случае определяется как максимум из значений RPL и CPL. Обращение к запрашиваемому сегменту данных разрешается, если его уровень защиты DPL max(RPL,CPL). Нарушение этого правила при обращении вызывает исключение типа #GP «нарушение общей защиты».
Таким образом, задавая определенное значение RPL в селекторе, можно управлять доступом текущей программы к сегментам данных, имеющим различный уровень защиты. Например, при RPL=0 доступ к данным будет определяться значением CPL текущей программы. Если задать RPL=3, то при любом CPL программа может обратиться только к наименее защищенным сегментам данных, относящимся к кольцу 3.
Обращение к сегменту стека выполняется путем загрузки селектора в регистр SS. Этот селектор должен выбирать дескриптор сегмента с разрешенной записью, т.е. в байте доступа бит R/W должен быть установлен в 1. Значения RPL и DPL должны быть равны CPL. Нарушение этих правил (обращение к сегменту, для которого R/W=0; использование селекторов и дескрипторов, имеющих значения RPL и DPL, не равные CPL) приводит к исключению типа #GP. При обращении к дескриптору, имеющему значение бита присутствия P=0 (отсутствующий сегмент) возникает исключение типа #SS “ошибка обращения к стеку”.
Обращение к сегментам программ (передача управления) реализуется при выполнении инструкций межсегментного перехода JMP, вызова подпрограммы CALL и возврата из подпрограммы RET, прерывания INT и возврата из прерывания IRET. Правила обращения к сегментам программ зависят от значения бита подчиненности C в байте доступа дескриптора.
Обращение к подчиненным сегментам, для которых установлено значение C=1, допускается только из программ, имеющих такой же или более низкий уровень привилегий. Таким образом, в программах с текущим уровнем привилегий CPL могут выполняться межсегментные инструкции JMP, CALL с передачей управления подчиненному сегменту, имеющему DPLCPL. При такой передаче значение CPL сохраняется, т.е. сохраняется уровень привилегий вызывающей программы.
Примеры обращений из различных уровней привилегий к подчиненному и неподчиненному сегментам показаны на рис $6. Здесь разрешенные связи обозначены обычными стрелками, а запрещенные – обозначены стрелками из точек, причем возле каждой такой стрелки в кружочке указана ссылка на правило, которое она нарушает.
Обратите внимание, что селекторы разных уровней привилегий физически могут располагаться в сегменте данных любого уровня позволяющего их загрузку в регистр CS. Например, все селекторы могут располагаться в сегменте данных третьего уровня.
Обычно бит подчиненности C=1 устанавливается для сегментов, которые могут использоваться программами с различными уровнями привилегий, например для системных библиотек. При этом подчиненные сегменты должны размещаться в кольце защиты с номером, соответствующим максимальному уровню привилегий (минимальному значению CPL) вызывающих программ.
Обращение к неподчиненным сегментам, имеющим значение C=0, с помощью инструкций JMP и CALL допускается только в случае, если уровень привилегий текущей программы равен уровню защиты сегмента: CPL=DPL
|
|
|
|
|
|
|
|
3 |
Сегмент |
|
|
|
|
|
|
кода A |
|
С |
|
|
|
|
|
( |
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
С |
|
|
Сегмент |
|
2 |
С |
|
|
|
кода D |
|
|
кода B |
|
|
|
|
(DPL=2) |
|
|
( |
|
С |
|
Подчиненный |
|||
|
|
|
|
|
сегмент |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Селектор сегмента D1 (RPL=1) |
|
Неподчиненный |
||
1 |
С |
|
|
|
сегмент |
|
|
кода C |
|
|
|
|
Сегмент |
|
|
( CPL=1) |
|
Селектор сегмента E1 (RPL=1) |
|
|
кода E |
|
|
|
|
|
|
|
(DPL=1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Сегмент |
|
Селектор сегмента E0 (RPL=0) |
|
|
|
|
0 |
кода F |
|
|
|
|
|
|
(CPL=0) |
|
|
|
|
|
|
|
|
Уровень привилегий |
|
|
|
|
|
|
Рис. $6 Примеры обращений к подчиненному и неподчиненному сегментам из различных уровней привилегий. Стрелки из точек – запрещенные обращения: (1) RPL>CPL при C=0; (2) CPLDPL при C=0; (3) RPL>DPL при C=0; (4) CPL>DPL при C=1.
Обращение через шлюз используется для вызова неподчиненных сегментов программ с более высоким уровнем привилегий, чем текущая программа. Для реализации таких вызовов служат специально установленные точки входа в программы, которые называются шлюзами (или вентилями) вызова. Менее привилегированная процедура может вызвать более привилегированную, обращаясь к ней через дескриптор шлюза вызова, определяющий доступную точку входа. Этот способ обращения позволяет программам пользователя обращаться за обслуживанием к операционной системе. При этом допускаются обращения только к определенным процедурам, которые санкционируются путем введения в систему соответствующего шлюза. Тем самым исключается возможность несанкционированного обращения менее привилегированных процедур к более привилегированным, что позволяет защитить их от возможных искажений.
Для вызова более привилегированной программы инструкция CALL или JMP должна обратиться к хранящемуся в LDT дескриптору шлюза вызова, формат которого показан на рис.$7.
Байты 7,6 |
Байт 5 |
Байт 4 |
||||||||||||||||||||||||
|
|
|
|
|
|
|
|
|||||||||||||||||||
31 |
|
16 |
15 |
14 |
13 |
12 |
11 |
8 |
7 |
6 |
5 |
4 |
0 |
|||||||||||||
Смещ.в целевом сегменте [31:16] |
P |
gDPL |
0 |
TYPE |
0 |
0 |
0 |
WC |
||||||||||||||||||
Селектор целевого TSS |
Смещение в целевом сегменте[15:0] |
|||||||||||||||||||||||||
31 |
16 |
15 |
0 |
|||||||||||||||||||||||
|
|
|
|
|
|
|||||||||||||||||||||
Байты 3,2 |
Байты 1,0 |
|||||||||||||||||||||||||
Рис. $7. Формат дескриптора шлюза вызова программы
В байтах 2,3 дескриптора шлюза содержится селектор вызываемого сегмента программы, а байты 0, 1, 6, 7 задают относительный адрес инструкции, являющейся точкой входа в эту программу. Байт доступа 5 содержит бит присутствия P и поле уровня привилегий дескриптора шлюза gDPL (gate DPL), которые имеют такое же назначение, как и в дескрипторах сегментов. Поле TYPE в байте доступа шлюза (табл. $.1) содержит код 0100 (если вызываемая программа написана для процессора 80286), либо код 1100 (если программа написана для процессоров 386+). Поле WC (Word Count) в байте 4 указывает количество параметров, которые переносятся из стека, обслуживающего программы с текущим уровнем привилегий CPL, в стек, используемый программами с уровнем привилегий вызываемой программы. Параметры представляют собой 32-разрядные слова (16-разрядные для процессоров 80286), число которых может составлять от 0 до 31.
При вызове программ через шлюз должны выполняться следующие правила.
Значения RPL селектора, вызывающего шлюз, должно быть меньше или равно значению gDPL в байте доступа шлюза.
Значения CPL текущей программы должно быть меньше или равно значению gDPL в байте доступа шлюза.
Значение gDPL шлюза вызова должно быть больше или равно уровню DPL вызываемого сегмента программ: gDPLDPL.
Вызываемый сегмент программ должен иметь такой же или более высокий уровень привилегий DPL, чем текущая программа: DPLCPL.
Последнее правило реализуется при выполнении инструкции CALL с обращением к любым видам сегментов программ (подчиненным и неподчиненным) и инструкции JUMP, вызывающей подчиненный сегмент. Если инструкция JUMP осуществляет переход к неподчиненному сегменту программ, то необходимо выполнение правила DPL=CPL.
Примеры реализации этих правил при различных комбинациях значений RPL, CPL, gDPL, DPL иллюстрируются на рис.$8. Здесь разрешенные связи обозначены обычными стрелками, а запрещенные – обозначены стрелками из точек, причем возле каждой такой стрелки в кружочке указан номер правила, которое она нарушает.
Заметим, что селекторы и дескрипторы разных уровней привилегий физически могут располагаться в сегментах данных любого уровня позволяющего их загрузку в сегментные регистры. Например, все селекторы могут располагаться в сегменте данных третьего уровня, а дескрипторы в таблице GDT, имеющей PL=0.
Если эти правила выполняются, то после вызова дескриптора шлюза в сегментный регистр CS загружается селектор – байты 3,2 этого дескриптора. Этот селектор выбирает из LDT дескриптор вызываемого сегмента программы. При этом поле RPL селектора игнорируются, а вместо него в регистр CS в качестве CPL заносится значение DPL из дескриптора вызываемого сегмента. В регистр EIP из дескриптора шлюза загружается относительный адрес входа в программу – байты 0, 1, 6, 7 (для программ процессора 80286 в IP загружаются байты 0, 1).
Если вызванная программа имеет другой (более высокий) уровень привилегий, чем текущая, то при выполнении инструкций JMP, CALL с использованием шлюза вызова производится переключение стека. Выполняется обращение к стеку, организованному для программ с уровнем привилегий, соответствующим уровню вызванной программы. В этот стек последовательно вводятся старые значения SS и ESP; параметры, переносимые из старого стека; старые значения CS и EIP. Число переносимых параметров определяется полем WC шлюза вызова, причем выбираются последние параметры, загруженные в старый стек.
|
|
|
|
|
|
|
|
|||
|
|
|
Сел.шл. K |
|
|
Шлюз K |
|
|
||
3 |
С |
|
(RPL=3) |
|
|
(gDPL=3) |
|
Сегмент |
||
кода A |
|
|
|
|
|
кода F |
||||
(
|
|
С |
|
|
|
(DPL=3) |
||||
|
|
|
(RPL=3) |
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
||
|
|
|
Сел.шл.М2 |
|
Шлюз М |
|
|
|
||
2 |
С егмент |
|
( |
|
(gDPL=2) |
|
|
|
||
кода B |
|
|
|
|
|
|
|
|
||
( CPL=2) |
|
Сел.шл. N |
|
|
|
|
Шлюз N |
|||
|
|
|
(RPL=2) |
|
|
|
|
(gDPL=2) |
||
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
||
1 |
Сегмент |
|
Селектор |
|
|
|
|
|
||
к ода C |
|
шлюза М1 |
|
|
|
|
|
|||
(CPL=1) |
|
(RPL=1) |
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
||
0 |
|
|
|
|
Сегмент |
|
Сегмент |
|
||
|
|
|
|
кода D |
|
кода E |
|
|||
|
|
(DPL=0) |
|
(DPL=0) |
|
|||||
|
Уровень привилегий |
Подчиненный сегмент. Стек не переключается |
Не подчиненный сегмент. Стек переключается |
|||||||
Рис. $8. Варианты передачи управления между уровнями защиты, с использованием шлюзов вызова.
На рис. $9 показано переключение стека при программном вызове через шлюз со сменой уровня привилегий.
Переключение стека производится автоматически путем загрузки в регистры SS и ESP из сегмента состояния задачи TSS нового содержимого, которое определяет начальный адрес стека для программ данного уровня привилегий.
Необходимо отметить, что переключение стека реализуется только при обращении к неподчиненным сегментам программ (имеющих в дескрипторе значение бита C=0). Если через шлюз осуществляется вызов подчиненного сегмента программ (значение бита C=1), то переключение стека не производится.
|
|
|
|
|
||
|
|
|
|
|
||
|
Параметр 3 |
|
SS проц-ры А |
|
||
|
Параметр 2 |
|
ESP проц-ры А |
|
||
|
П |
ESP |
Параметр 3 |
|
||
|
|
|
Параметр 2 |
|
||
Стек вызывающей процедуры А |
|
Параметр 1 |
|
|||
|
CS проц-ры А |
|
||||
|
|
|
E |
ESP |
||
|
|
|
|
|
||
|
|
|
Стек вызванной процедуры B, после ее вызова |
|||
|
|
|
||||
Рис. $9 Переключение стека при вызове через шлюз со сменой уровня привилегий
По инструкции RET производится восстановление из стека старого содержимого регистров CS, EIP (IP) и SS, ESP (SP). Таким образом, одновременно с возвратом к исходной программе происходит возвращение к старому стеку. Инструкция RET проверяет значение CPL и поле RPL извлекаемого из стека старого содержимого CS, определяющие уровень привилегий программы, к которой осуществляется возврат. Инструкция выполняется только в том случае, когда значение этих битов больше или равно CPL, т.е. возврат осуществляется к программе с таким же или меньшим уровнем привилегий.
Нарушение инструкциями JMP, CALL, RET указанных правил обращения к сегментам программ вызывает исключение типа #GP (нарушение защиты).
Чтобы избежать частых нарушений хода выполнения программы из-за несоблюдения правил доступа к сегментам, в набор инструкций процессора введены специальные инструкции, используемые для проверки различных условий доступа. Инструкция LAR осуществляет загрузку в регистр байта доступа сегмента, выбираемого с помощью селектора, который содержится в адресуемой ячейке памяти или регистре. После этой операции процессор может произвести побитный анализ содержимого байта доступа, определяя возможности обращения к данному сегменту.
Инструкция LSL загружает в регистр значение границы сегмента, который выбирается таким же образом, как инструкцией LAR. Используя полученное значение этой границы, процессор может организовать контроль формирования адреса, чтобы избежать прерывания программы при выходе за пределы адресуемого сегмента.
Инструкции VERR, VERW проверяют возможности записи в сегмент данных и чтения из сегмента программ, устанавливаемые соответствующими битами дескрипторов. Если запись или чтение разрешены, то устанавливается значение признака ZF=1 в регистре EFLAGS. Предварительный анализ значения этого признака позволяет избежать запрещенных видов обращения к данному сегменту, вызывающих прерывание текущей программы. Адресация необходимого сегмента при выполнении инструкций VERR, VERW производится таким же образом, как и инструкцией LAR.
Реализация ввода – вывода с помощью инструкций IN, OUT, INS, OUTS в защищенном режиме производится с учетом CPL выполняемой программы. Величина CPL сравнивается со значением поля IOPL в регистре EFLAG. Ввод-вывод производится только при выполнении условия CPLIOPL. Нарушение этого условия вызывает исключение типа #GP, если доступ к адресованному инструкцией порту не разрешен специальной битовой картой ввода/вывода, задаваемой при решении отдельных задач.

електор
сегмента D3 (RPL=3)
CPL=3)
електор
сегмента D2 (RPL=2)
егмент
CPL=2)
електор
сегмента E2 (RPL=2)
егмент
егмент
CPL=3)
ел.шл.М3
RPL=2)
араметр
1
IP
проц-ры А