В защищенном режиме процессор имеет четырехуровневую систему привилегий, которая управляет использованием привилегированных инструкций и доступом к сегментам памяти. Уровень привилегии сегмента указывается в его дескрипторе. Нумерация уровней происходит от 0 до 3, высшие привилегии соответствуют нулевому уровню. Для наглядности их принято изображать в виде колец.
Уровни привилегий обеспечивают защиту целостности операционной системы от «недружелюбных» программ. А также защиту адресных пространств одновременно выполняемых задач. Сервисы операционной системы, обработчики прерываний и другое системное обеспечение могут включаться в виртуальное адресное пространство каждой задачи и защищаться системой привилегий. Как правило, назначение уровней следующее. Нулевой уровень – уровень операционной системы (ядро операционной системы). Уровни 1 и 2 – программы системного назначения. Уровень 3 – прочие прикладные программы (уровень прикладных программ). Дескрипторы (сегменты) и селекторы имеют свои атрибуты привилегий.
Привилегии задач оказывают
влияние на выполнение инструкций и использование дескрипторов. Текущий уровень
привилегии задачи CPL (Current
Privilege Level) определяется двумя
младшими битами регистра CS.
CPL задачи может изменяться
только при передаче управления к новому сегменту через дескриптор шлюза (см. главу 5). Задача начинает выполняться с
уровня CPL, указанного селектором
кодового сегмента внутри TSS, когда задача инициируется
посредством операции переключения задач. Задача, выполняемая на нулевом уровне
привилегий, имеет доступ ко всем сегментам, описанным в GDT, и является самой привилегированной. Задача, выполняемая на уровне 3,
имеет самые ограниченные права доступа. Текущий уровень привилегии задачи может
изменяться только при передаче управления через шлюзы.
Привилегии сегмента задаются полем DPL байта управления доступом.
DPL определяет
наибольший номер уровня привилегий (фактически, наименьшие привилегии), с
которым возможен доступ к данному дескриптору. Самый защищенный
(привилегированный) дескриптор имеет DPL=0, к нему имеют доступ только задачи с CPL=0. Самый общедоступный дескриптор
имеет DPL=3, его могут
использовать задачи с CPL=0, 1, 2, 3. Это правило применимо ко всем дескрипторам, за исключением дескриптора
LDT.
Привилегии селектора задаются полем RPL (Requested
Privilege Level) — двумя младшими битами
селектора.
При обращении в сегмент данных с помощью RPL можно ограничить привилегии
(относительно CPL), вводится понятие эффективный
уровень привилегий EPL (Effective
Privilege Level), который определяется как
максимальное из значений CPL и RPL, Селектор с RPL=0 не вводит дополнительных
ограничений.
EPL = MAX ( CPL, RPL )
Контроль доступа к сегментам данных производится при исполнении команд, загружающих селекторы в SS, DS, ES, FS и GS. Команды загрузки DS, ES, FS и GS должны ссылаться на дескрипторы сегментов данных или сегментов кодов,
допускающих чтение. Для получения доступа эффективный уровень привилегий EPL должен быть равным или меньшим
(арифметически) уровня привилегий DPL дескриптора.
Исключением из
этого правила является читаемый подчиненный сегмент кода, который может быть
прочитан задачей с любым CPL. Если эффективный уровень привилегий не
разрешает доступ или ссылка производится на некорректный тип дескриптора (на
дескриптор шлюза или на дескриптор только исполняемого кодового сегмента),
вырабатывается исключение #GP. При ссылке на
несуществующий дескриптор вырабатывается исключение #NP.
Команды
загрузки SS должны ссылаться на дескриптор сегмента данных, допускающий
запись. При этом DPL и RPL
должны быть равны CPL. Нарушение этого условия и ссылка на дескриптор
другого типа порождают исключение #GP, при ссылке на несуществующий
дескриптор вырабатывается исключение #SS.
Контроль типов и привилегий при передаче управления производится при загрузке селектора в регистр CS. Тип дескриптора, на который ссылается данный селектор, должен
соответствовать выполняемой инструкции. Нарушение типа (например, ссылка
инструкции JMP на шлюз вызова) порождает исключение #GP. При передаче управления действуют следующие правила привилегий,
нарушение которых также приводит к исключению #GP:
· Команды JMP или CALL могут ссылаться либо на
подчиненный сегмент кода с DPL, большим или равным CPL, либо на
неподчиненный сегмент с DPL равным CPL;
· Прерывания внутри задачи или вызовы, которые могут изменить уровень
привилегий, могут передавать управление кодовому сегменту с уровнем привилегий,
равным или большим уровня привилегий CPL, только через шлюзы с тем же
или меньшим уровнем привилегий, чем CPL;
· Инструкции возврата, которые не переключают задачи, могут передать
управление только кодовому сегменту с таким же или меньшим уровнем привилегий;
· Переключение задач может выполняться с помощью вызова, перехода или
прерывания, которые ссылаются на шлюзы задачи или сегмент состояния задачи (TSS) с тем же или меньшим уровнем привилегий.
Смена уровня
привилегий, происходящая при передаче управления, автоматически вызывает
переопределение стека. Начальное значение указателя стека SS:SP для уровня привилегий 0, 1, 2
содержится в TSS. При передаче управления по командам JMP или CALL в CS:SP загружается новое значение
указателя стека, а старые значения помещаются в новый стек. При возврате на
прежний уровень привилегий его стек восстанавливается (как часть инструкции RЕТ
или IRЕТ). Для вызовов подпрограмм с передачей параметров через стек и
сменой уровня привилегий из предыдущего стека в новый копируется фиксированное
число слов, заданное в шлюзе. Команда межсегментного возврата RET с выравниванием указателя
стека при возврате корректно восстановит значение предыдущего указателя.
Привилегии и битовая карта
разрешения ввода/вывода контролируют возможность
выполнения операций ввода/вывода и управления флагом прерываний IF. Уровень привилегий ввода/вывода определяется полем IOPL (Input/Output
Privilege Level) регистра флагов. Значение IOPL можно изменить только при CPL=0 (только для операционной системы).
При CPL £ IOPL на операции
ввода/вывода и управление флагом IF никаких ограничений не накладывается. При CPL > IOPL попытка ввода/вывода, выполненная задачей с TSS класса 80286,
вызывает исключение #GP (отказ). Если CPL > IOPL, а с задачей связан TSS 386+, инструкции ввода/вывода могут выполняться только по адресам
портов, для которых установлены нулевые биты в карте разрешения ввода/вывода,
имеющейся в TSS (см. главу 5). Попытки обращения
к портам, которым соответствуют единичные биты карты или которые не попали в
карту (ее размер может усекаться), вызывают исключение #GP.
При CPL > IOPL попытка выполнения инструкций CLI и STI вызывает исключение #GP. Неявное управление флагом
прерываний инструкциями загрузки или восстановления регистра флагов блокируется
без генерации исключений. Вопросы виртуализации прерываний будут рассмотрены
ниже.
Для надежной
работы многозадачных систем необходима защита задач друг от друга. Защита предназначена
для предотвращения несанкционированного доступа к памяти и выполнения
критических инструкций — команды HLT, которая останавливает
процессор, команд ввода/вывода, управления флагом разрешения прерываний и
команд, влияющих на сегменты кода и данных. Механизмы защиты вводят следующие
ограничения:
· Ограничение использования
сегментов (например, запрет записи в только читаемые сегменты данных или
попытки исполнения данных как кода). Для использования доступны только
сегменты, дескрипторы которых описаны в GDT и LDT;
· Ограничение доступа к
сегментам через правила привилегий;
· Ограничение набора инструкций — выделение привилегированных инструкций или операций, которые можно выполнять
только при определенных уровнях CPL и IOPL;
· Ограничение возможности межсегментных вызовов и передачи управления.
В защищенном
режиме при исполнении инструкций процессор выполняет проверки условий, порождающих исключения:
· Превышение лимита таблицы дескрипторов — #GP;
· Несуществующий дескриптор сегмента — #NP или #SS;
· Нарушение привилегий — #GP;
· Загрузка неверного дескриптора или типа сегмента — #GP:
§ загрузка в SS сегмента кода или сегмента
данных только для чтения;
§ загрузка управляющих дескрипторов в DS, ES или SS;
§ загрузка только исполняемых сегментов в DS, ES или SS;
§ загрузка сегмента данных в CS.
· Запись в
сегмент кода или сегмент данных только для чтения -#GP.
· Чтение из
только исполняемого сегмента кодов - #GP.
· Превышение
лимита сегмента — #SS или #GP.
· CPL <> 0 при выполнении инструкций LIDT, LLDT, LGDT, LTR, LMSW, CTS, HLT, INVD, INVLPG, WBINVD, операции с регистрами DRn, TRn, CRn - #GP;
· CPL > IOPL при выполнении инструкций STI, CLI, а для 80286 еще и инструкции LOCK — #GP;
· CPL > IOPL при выполнении инструкций IN,
INS, OUT, OUTS с портами, неразрешенными
битовой картой ввода/вывода — #GP.
При выполнении
команд IRET и POPF с недостаточным уровнем
привилегий биты IF и IOPL в регистре флагов не изменяются,
исключения не порождаются:
· IF не меняется, при CPL > IOPL;
· IOPL не меняется, если CPL > 0.
Проверки при передаче управления по инструкциям JMP, CALL, RET, INT и IRET включают как проверку ссылок
по размеру (в «ближних» формах JMP, CALL и RET выполняются только эти
проверки), так и проверку правил привилегий при межсегментных передачах через
шлюзы.
Для того чтобы
задачи не «нарывались» на срабатывание защиты, в систему команд введены
специальные инструкции тестирования
указателей. Они позволяют быстро удостовериться в возможности
использования селектора или сегмента без риска порождения исключения:
· ARPL — выравнивание RPL. При ее исполнении RPL селектора приравнивается
максимальному значению из текущего RPL
селектора и поля RPL в указанном регистре.
Если при этом RPL изменился,
устанавливается ZF=1;
· VERR — проверка
возможности чтения: если сегмент, на который указывает селектор, допускает
чтение, устанавливается ZF=1;
· VERW — проверка
возможности записи: если сегмент, на который указывает селектор, допускает
запись, устанавливается ZF=1;
· LSL — чтение лимита сегмента в
регистр, если позволяют привилегии. При успехе устанавливается ZF=1;
· LAR — чтение байта доступа
дескриптора в регистр, если позволяют привилегии. При успехе устанавливается ZF=1.
Некоторые
функции защиты выполняются и механизмом страничной переадресации, однако, в
отличие от «непробиваемой» сегментной защиты, существуют способы обхода
страничной защиты на уровне пользователя (CPL=3).