Система команд x86 |
|
Программирование - Архитектура и система команд микропроцессоров x86 |
INT n
Влияние команды на флаги и форматы команды:
|
|
0 |
0 |
|
|
|
|
|
CC |
INT 3 |
Вызов прерывания 3 (#BP, точка останова) |
8086 |
int 3 |
CD ib |
INT imm8 |
Вызов прерывания imm8 |
8086 |
int 13 |
CE |
INTO |
Вызов прерывания 4 (#OF, переполнение), если EFLAGS.OF=1 |
8086 |
into |
Описание:
Команда INT n обеспечивает программную генерацию прерывания и передачу управления в соответствующий обработчик. Непосредственный операнд команды может принимать значения от 0 до 255, что задает вектор (индекс) вызываемого прерывания по таблице дескрипторов прерываний (IDT) или по таблице векторов прерываний (IVT). При этом первые 32 вектора прерываний (от 0 до 31) зарезервированы Intel для системного использования. Большинство из них используется для обработки генерируемых процессором особых ситуаций. Команда INT n предназначена для программной генерации прерываний с номерами от 32 до 255, но технически возможно вызвать и прерывания с меньшими (любыми) векторами. В этом случае процессор ведет себя не так, как это описано для соответствующего внешнего прерывания или особой ситуации, а так, как это предусмотрено именно для генерируемых программно прерываний пользователя (то есть не записываются никакие коды ошибок, не соблюдаются приоритеты и т.п.).
Команда INT n в общем ведет себя как дальний вызов командой CALL, за исключением того, что регистр флагов помещается в стек перед адресом возврата. Процедуры обработки прерываний завершаются командой IRET/IRETD, которая выбирает из стека флаги и адрес возврата. В деталях же реакция процессора на поступление программного прерывания определяется его текущим режимом работы (см. Прерывания и особые ситуации: Реальный режим, Защищенный режим, Режим V86).
В режиме реальной адресации таблица векторов прерываний (IVT), которую иногда называют просто таблицей прерываний, является массивом 4-байтных дальних указателей (см. рис. 3.1). Конечным результатом исполнения команды INT n в этом случае и является переход (передача управления) по заданному адресу. Процессор автоматически выбирает из памяти соответствующий указатель, затем сохраняет в стеке текущие (16-битные) значения FLAGS, CS, IP (см. рис. 3.2.), очищает биты IF, TF, AC регистра FLAGS и передает управление по считанному адресу. Линейный базовый адрес таблицы прерываний определяется содержимым регистра IDTR. Первоначальное значение IDTR при сбросе процессора или переходе в режим реальной адресации является нулевым.
Рис. 3.1. Таблица векторов прерываний в реальном режиме работы
Рис. 3.2. Стек после вызова прерывания или особой ситуации в реальном режиме работы
В защищенном режиме таблица дескрипторов прерываний (IDT) состоит из множества 8-байтных дескрипторов (см. рис. 3.3). Дескрипторы в таблице могут иметь разные типы. Допустимы: шлюз прерывания, шлюз ловушки или шлюз задачи. При поступлении команды прерывания процессор сохраняет текущие значения EFLAGS, CS и EIP в стеке. Когда передача управления в обработчик связана с изменением уровня привилегий на более высокий (меньшее числовое значение), это сопровождается переключением стека. Новые значения для SS и ESP берутся из TSS текущей задачи в соответствии с уровнем привилегий обработчика. Переключение стека производится до сохранения указанных выше регистров, причем в новом стеке в первую очередь сохраняются значения SS и ESP, указывающие на старый стек (см. рис. 6.10.).
Передавая управление в обработчик, процессор проверяет уровень привилегий DPL соответствующего шлюза с тем, чтобы предотвратить генерацию программных прерываний, если CPL > DPL. Если обработчик прерывания защищенного режима будет расположен в согласованном кодовом сегменте, то ему при получении управления будет доступен только сегмент стека и те сегменты, привилегии которых позволяют это. Если же обработчик будет размещен в несогласованном кодовом сегменте с нулевым уровнем привилегий, то независимо от CPL прерванной программы ему будут доступны все сегменты.
При передаче управления через шлюз прерывания или шлюз ловушки процессор сбрасывает флаги EFLAGS.TF, EFLAGS.RF и EFLAGS.NT, чтобы отключить трассировку прерываний и некоторые особенности обработки вложенных задач. Кроме того, при использовании шлюза прерывания сбрасывается также и флаг разрешения прерываний EFLAGS.IF.
В защищенном режиме (как в и режиме реальной адресации) линейный базовый адрес таблицы IDT определяется содержимым регистра IDTR (см. рис. 3.3).
Рис. 3.3. Таблица дескрипторов прерываний (IDT) в защищенном режиме работы
Рис. 6.10. Стек после вызова программного прерывания в защищенном режиме
При поступлении программного прерывания в режиме V86 процессор автоматически переключается в защищенный режим с уровнем привилегий CPL = 0 (в связи с этим происходит замена указателя стека SS:ESP на стек нулевого уровня привилегий из сегмента состояния задачи TSS) и обращается к соответствующему дескриптору прерывания в таблице IDT. Если в качестве такого дескриптора используется 32-битный шлюз ловушки или шлюз прерывания, то он должен указывать на несогласованный кодовый сегмент с нулевым уровнем привилегий (уровень привилегий DPL соответствующего шлюза должен быть установлен в 3, а CPL процедуры обработки прерывания должен быть равен 0, чтобы обеспечить обработку прерывания защищенного режима на нулевом уровне привилегий), в противном случае генерируется ошибка общей защиты (#GP) с селектором этого сегмента в коде ошибки.
После переключения на нулевой уровень привилегий и загрузки новых значений указателя стека SS:ESP процессор сохраняет в стеке текущие значения сегментных регистров GS, FS, DS, ES, а затем очищает эти регистры, помещая в них нулевые селекторы. Далее действия процессора идентичны процедуре обработки прерываний защищенного режима — он сохраняет в стеке старые значения SS, ESP, EFLAGS, CS и EIP, сбрасывает флаги VM, NT, TF, RF и IF (последний сбрасывается, только если используется шлюз прерывания) регистра флагов EFLAGS и передает управление в обработчик прерывания (см. рис. 6.11.).
Если при вызове программных прерываний командой INT n текущий уровень привилегий ввода/вывода EFLAGS.IOPL меньше текущего уровня привилегий задачи (то есть EFLAGS.IOPL < 3), то генерируется ошибка общей защиты (#GP) и управление передается в обработчик этой ошибки. Такое поведение называется IOPL-чувствительностью и в режиме V86 характерно также для команд CLI, STI, POPF/POPFD, PUSHF/PUSHFD, IRET/IRETD.
При возврате из прерывания по завершении работы программы обработчика защищенного режима процессор получает команду IRETD. Последовательно загружая сохраненные в стеке значения, он считывает и значение флага EFLAGS.VM. При обнаружении единицы в этом флаге производится обратное переключение в режим V86 и восстановление из стека сегментных регистров ES, DS, FS, GS, после чего управление передается в прерванную программу. Если команда IRETD будет получена на уровне привилегий, отличном от нуля, то процессор не сможет установить флаг EFLAGS.VM – и соответственно – не переключится в режим V86 — возврат будет произведен некорректно.
Рис. 6.11. Стек после вызова прерывания в режиме V86
В процессорах, начиная с Pentium, реализуются специальные расширения системы обработки прерываний в защищенном режиме и режиме V86. Это так называемые виртуальные прерывания защищенного режима (включаются при CR4.PVI = 1) и режим EV86 (включается при CR4.VME = 1). В защищенном режиме активация виртуальных прерываний никак не влияет на порядок обработки программных прерываний и особых ситуаций, генерируемых командой INT n (равно как и командами INTO, INT 3, INT01, BOUND). А вот в режиме EV86 включается совершенно другой механизм обработки программных прерываний, который подразумевает наличие четырех разных режимов, определяемых текущими установками флагов и управляющих параметров процессора и конкретной задачи V86:
- Режим 2 — для программных прерываний, когда EFLAGS.IOPL < 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 1;
- Режим 3 — для программных прерываний и внешних маскируемых прерываний, когда EFLAGS.IOPL = 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 0;
- Режим 4 — для программных прерываний и внешних маскируемых прерываний, когда EFLAGS.IOPL = 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 1;
- Режим 5 — для программных прерываний, когда EFLAGS.IOPL < 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 0.
Указанные режимы действуют только для команд генерации программных прерываний INT n, имеющих двухбайтный код операции (CD ib). Однобайтные команды INTO, INT 3, INT01, а также команда BOUND относятся к командам генерации программных особых ситуаций и на них не распространяется действие расширений режима EV86.
Таблица 6.54. показывает, какое действие из нижней части таблицы происходит в случае выполнения условий из верхней части таблицы. Каждый символ "+" в нижней части указывает на то, что выполняется соответствующая процедура, как это определено в подпункте Операция, в противном случае имеет место ошибка общей защиты #GP(0).
Таблица 6.54. Возможные варианты обработки прерываний
(согласно подпункта Операция)
Режим реальной адресации | Защищенный режим | Режим V86 | |||||||||||
Режим EV86 | |||||||||||||
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | |
x | x | x | x | x | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | |
CR4.VME | x | x | x | x | x | x | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
IRB[n] | x | x | x | x | x | x | x | x | 0 | x | 0 | 1 | 1 |
x | x | x | x | x | x | < 3 | = 3 | < 3 | x | = 3 | < 3 | = 3 | |
x | > DPLш | – | – | – | – | 3 | 3 | x | x | x | x | x | |
DPL шлюза | x | < CPL | – | – | – | – | x | x | x | x | x | x | x |
DPL нового кодового сегмента | – | – | – | > CPL | = CPL | < CPL | – | – | – | – | – | – | – |
Тип прерывания | – | П1 | – | – | – | – | П | – | П | А, О1 | П | П | П |
Тип дескриптора (шлюза) прерывания |
IVT | – | З | Л, П | Л, П | Л, П | Л, П | Л, П | IVT | Л, П | IVT | Л, П | Л, П |
Режим реальной адресации: REAL-ADDRESS-MODE |
+ | ||||||||||||
Защищенный режим: PROTECTED-MODE |
+ | + | + | + | + | + | + | + | + | ||||
Шлюз ловушки или прерывания: |
+ | + | + | + | + | + | + | ||||||
Прерывание на уровень с большими привилегиями: INT-TO-INTER-PRIV |
+ | ||||||||||||
Прерывание без изменения привилегий: INT-TO-INTRA-PRIV |
+ | ||||||||||||
Прерывание из режима V86: |
+ |
+ |
+ |
||||||||||
Прерывание в режиме EV86: EV86-MODE |
+ (р.5) |
|
+ (р.3) |
|
|
||||||||
Шлюз задачи: TASK-GATE |
+ | ||||||||||||
Ошибка общей защиты (#GP) | + | + | + |
+ |
|||||||||
Условно-выраженная команда INTO в основном идентична команде INT n за исключением того, что номер прерывания задается неявно равным 4 и программная особая ситуация Переполнение (#OF) генерируется, только если установлен флаг переполнения OF.
Аналогичным образом, командой INT 3 генерируется Прерывание 3 — Точка останова (#BP). Это прерывание предназначено для использования отладчиком, который размещает специальную однобайтную команду INT 3 (код CCh) вместо первого байта команд или вместо однобайтных команд. Существует второй способ вызова такого прерывания — с помощью двухбайтного кода INT 3 (код CD03h). Однако данный метод на практике не применяется, все ассемблеры x86 по умолчанию интерпретируют мнемонику INT 3 как однобайтную команду с кодом CCh (но это не исключает возможность ручного программирования двухбайтного кода). Помимо размера кода, отличается и процесс обработки одно- и двухбайтных команд INT 3.
Прерывание, сгенерированное любой однобайтной командой (INTO, INT 3, INT01) или командой BOUND, в режиме EV86 (CR4.VME = 1) не подвергается перенаправлению по карте перенаправления прерываний (как это описано для Режима 2, Режима 3, Режима 5) и всегда обрабатывается обработчиком защищенного режима через обращение к соответствующему дескриптору в таблице IDT (то есть перманентно используется Режим 4). Кроме того, в режиме V86 для таких прерываний не осуществляется проверка поля EFLAGS.IOPL и, соответственно, не генерируется ошибка общей защиты #GP, когда EFLAGS.IOPL < 3 (то есть указанные команды не являются IOPL-чувствительными).
Операция:
Представленный здесь алгоритм описывает не только поведение процессора при выполнении команды INT n, но и при поступлении любого внешнего прерывания или генерации особой ситуации.
IF CR0.PE = 0
THEN GOTO REAL-ADDRESS-MODE;
ELSE
IF ( EFLAGS.VM = 1 AND EFLAGS.IOPL < 3 AND
( INT n ) AND
( CR4.VME = 0 OR CR4.VME = 1 AND IRB[n] = 1 )
) (* IRB[n] - бит, соответствующий прерыванию n в карте перенаправления прерываний *)
THEN
#GP(0); (Программное прерывание INT n в режиме: (1) V86 при EFLAGS.IOPL < 3, (2) EV86 Режим 2 *)
ELSE (* Защищенный режим или режим V86/EV86 *)
IF ( EFLAGS.VM = 1 AND CR4.VME = 1 AND
( INT n ) AND IRB[n] = 0 )
THEN GOTO EV86-MODE; (* EV86: Режим 3 или Режим 5 *)
ELSE GOTO PROTECTED-MODE; (* Аппаратные прерывания, особые ситуации; программные прерывания INT n в режиме: (1) Защищенный режим, (2) V86 при EFLAGS.IOPL = 3, (3) EV86 Режим 1 или Режим 4 *)
FI;
FI;
FI;
END;
REAL-ADDRES-MODE:
IF ( (Номер прерывания * 4) + 3 выходит за пределы сегмента при обращении к таблице векторов прерываний IVT ) THEN #GP; FI;
IF ( В стеке нет места для 6 байт ) THEN #SS; FI;
Push(FLAGS);
EFLAGS.IF = 0; (* Сброс флага прерываний *)
EFLAGS.TF = 0; (* Сброс флага ловушки *)
EFLAGS.AC = 0; (* Сброс флага режима контроля выравнивания *)
Push(CS);
Push(IP);
(* В стек не заносятся коды ошибок *)
CS = IVT[Номер прерывания * 4].selector;
EIP = IVT[Номер прерывания * 4].offset AND 0x0000FFFFh; (* старшие 16-бит регистра EIP обнуляются *)
(* Продолжение работы в режиме реальной адресации ... *)
END;
EV86-MODE: (* CR0.PE = 1, EFLAGS.VM = 1, CR4.VME = 1, Режим EV86 - программное прерывание INT n, при IRB[n] = 0 - Режим 3 или Режим 5 *)
IF (В стеке задачи V86 нет места для 6 байт) THEN #SS(0); FI;
tempFLAGS = FLAGS;
tempFLAGS.IOPL = 0; (* см. Примечание *)
tempFLAGS.NT = 0;
IF EFLAGS.IOPL = 3 (* Режим 3 *)
THEN EFLAGS.IF = 0; (* Сброс флага разрешения прерываний *)
ELSE (* EFLAGS.IOPL < 3 - Режим 5 *)
tempFLAGS.IF = EFLAGS.VIF;
EFLAGS.VIF = 0; (* Сброс виртуального флага прерываний *)
FI;
EFLAGS.TF = 0; (* Сброс флага ловушки *)
Push(tempFLAGS);
Push(CS);
Push(IP);
(* В стек не заносятся коды ошибок *)
CS = IVT_V86[Номер прерывания * 4].selector; (* Таблица векторов прерываний IVT_V86 располагается в начале адресного пространства задачи V86 *)
EIP = IVT_V86[Номер прерывания * 4].offset AND 0x0000FFFFh; (* старшие 16-бит регистра EIP обнуляются *)
(* Продолжение работы в режиме EV86 ... *)
END;
PROTECTED-MODE: (* CR0.PE = 1, Аппаратные прерывания, особые ситуации; программные прерывания INT n в режиме: (1) Защищенный режим, (2) V86 при EFLAGS.IOPL = 3, (3) EV86 Режим 1 или Режим 4 *)
IF ( (Номер прерывания * 8) + 7 не попадает в пределы таблицы IDT ) THEN #GP(номер прерывания * 8 + 2 + EXT); FI;
(* Здесь и далее в параметрах кода ошибки слагаемое +2 означает установку бита IDT кода ошибки, а слагаемое +EXT - означает установку бита EXT кода ошибки в соответствии с тем, было ли вызвавшее ошибку прерывание программным EXT = 0 или внешним EXT = 1 *)
Байт AR дескриптора должен задавать шлюз прерывания, шлюз ловушки или шлюз задачи, иначе #GP(номер прерывания * 8 + 2 + EXT);
IF (Программное прерывание или особая ситуация) (* Т.е. один из случаев INT n, INT 3, INT01, BOUND или INTO *)
THEN
IF ( CPL > DPL шлюза )
THEN
#GP(номер прерывания * 8 + 2); (* CR0.PE = 1, DPL шлюза < CPL, программное прерывание *)
FI;
FI;
Шлюз должен присутствовать, иначе #NP(номер прерывания * 8 + 2 + EXT);
IF (Шлюз задачи)
THEN GOTO TASK-GATE;
ELSE
GOTO TRAP-OR-INT-GATE; (* CR0.PE = 1, шлюз прерывания или ловушки *)
FI;
END;
TRAP-OR-INT-GATE: (* Защищенный режим или режим V86/EV86, шлюз ловушки или прерывания *)
Проверка нового селектора CS, заданного в дескрипторе шлюза, и соответствующего ему дескриптора из LDT или GDT:
Селектор должен быть не нулевым, иначе #GP(EXT);
Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #GP(селектор + EXT);
Выбранный дескриптор должен быть дескриптором сегмента кода, иначе #GP(селектор + EXT);
Сегмент должен присутствовать (P = 1), иначе #NP(селектор + EXT);
IF (Кодовый сегмент несогласованный) AND (DPL сегмента кода < CPL)
THEN
IF EFLAGS.VM = 0
THEN GOTO INT-TO-INTER-PRIV; (* CR0.PE = 1, EFLAGS.VM = 0, шлюз прерывания или ловушки, несогласованный кодовый сегмент, DPL сегмента кода < CPL *)
ELSE (* EFLAGS.VM = 1 *)
IF ( DPL нового сегмента кода ≠ 0 ) THEN #GP(Селектор кодового сегмента + EXT); FI;
GOTO INT-FROM-V86-MODE;(* CR0.PE = 1, EFLAGS.VM = 1, шлюз прерывания или ловушки, DPL сегмента кода = 0, CPL = 3 *)
FI;
ELSE (* CR0.PE = 1, шлюз прерывания или ловушки, согласованный кодовый сегмент или несогласованный кодовый сегмент с DPL = CPL *)
IF EFLAGS.VM = 1 THEN #GP(Селектор кодового сегмента + EXT); FI;
IF ( (Кодовый сегмент согласованный) OR (DPL сегмента кода = CPL) )
THEN GOTO INT-TO-INTRA-PRIV; (* CR0.PE = 1, шлюз прерывания или ловушки, DPL сегмента кода ≤ CPL для согласованного сегмента, DPL сегмента кода = CPL для несогласованного сегмента *)
ELSE #GP(Селектор кодового сегмента + EXT); (* DPL > CPL для согласованного сегмента или DPL ≠ CPL для несогласованного сегмента *)
FI;
FI;
END;
INT-TO-INTER-PRIV: (* Защищенный режим, шлюз прерывания или ловушки, несогласованный кодовый сегмент, DPL сегмента кода < CPL *)
IF ( Текущий TSS 32-битный )
THEN
TSSstackAddress = (new code segment DPL * 8) + 4
IF ( (TSSstackAddress + 5) > Предел TSS ) (* (TSSstackAddress + 7) > Предел TSS - для некоторых моделей процессоров *)
THEN #TS(Селектор текущего TSS + EXT);
FI;
NewSS = [База TSS + TSSstackAddress + 4]; (* загружается 2 байта *)
NewESP = [База TSS + TSSstackAddress]; (* загружается 4 байта *)
ELSE (* Текущий TSS 16-битный *)
TSSstackAddress = (new code segment DPL * 4) + 2
IF ( (TSSstackAddress + 3) > Предел TSS ) (* (TSSstackAddress + 4) > Предел TSS - для некоторых моделей процессоров *)
THEN #TS(Селектор текущего TSS + EXT);
FI;
NewESP = [База TSS + TSSstackAddress]; (* загружается 2 байта *)
NewSS = [База TSS + TSSstackAddress + 2]; (* загружается 2 байта *)
FI;
Проверка селектора нового стекового сегмента NewSS и соответствующего ему дескриптора из LDT или GDT:
Селектор должен быть не нулевым, иначе #TS(EXT);
Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #TS(SS селектор + EXT);
RPL селектора должен быть равен DPL нового кодового сегмента, иначе #TS(SS селектор + EXT);
DPL стекового сегмента должен быть равен DPL нового кодового сегмента, иначе #TS(SS селектор + EXT);
Дескриптор должен иметь формат дескриптора сегмента данных, разрешенного для записи (W = 1), иначе #TS(SS селектор + EXT);
Сегмент должен присутствовать (P = 1), иначе #SS(SS селектор + EXT);
IF (32-битный шлюз)
THEN Новый стек должен иметь место для 20 байт (24 байт, если есть код ошибки), иначе #SS(EXT)
ELSE Новый стек должен иметь место для 10 байт (12 байт, если есть код ошибки), иначе #SS(EXT)
FI;
Новый указатель инструкции должен попадать в пределы нового кодового сегмента, иначе #GP(EXT); (* указатель инструкции определяется значением поля OFFSET из дескриптора шлюза *)
SS:ESP = TSS(NewSS:NewESP); (* Загрузить новые значения SS и eSP из TSS *)
IF (32-битный шлюз)
THEN CS:EIP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 32-битного шлюза *)
ELSE CS:IP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 16-битного шлюза *)
FI;
Загрузить дескриптор CS в скрытую часть регистра CS;
Загрузить дескриптор SS в скрытую часть регистра SS;
IF (32-битный шлюз)
THEN
Push(Длинный указатель на старый стек - SS:ESP); (* 3 слова дополняются до 4 *)
Push(EFLAGS);
Push(Длинный указатель на точку возврата - CS:EIP); (* 3 слова дополняются до 4 *)
Push(Код ошибки); (* Если присутствует, 4 байта *)
ELSE
Push(Длинный указатель на старый стек - SS:SP); (* 2 слова *)
Push(FLAGS);
Push(Длинный указатель на точку возврата - CS:IP); (* 2 слова *)
Push(Код ошибки); (* Если присутствует, 2 байта *)
FI;
CPL = DPL нового кодового сегмента;
CS.RPL = CPL;
IF (Шлюз прерывания) THEN EFLAGS.IF = 0 FI; (* Сбросить флаг прерывания *)
EFLAGS.TF = 0;
EFLAGS.NT = 0;
EFLAGS.RF = 0;
(* Продолжение работы в защищенном режиме на уровне с большими привилегиями ... *)
END;
INT-FROM-V86-MODE: (* Режим V86/EV86, шлюз прерывания или ловушки, DPL = 0, CPL = 3 *)
(* Текущий TSS всегда 32-битный в режиме V86 *)
IF ( Предел TSS < 9 ) (* Предел TSS < 11 - для некоторых моделей процессоров *)
THEN #TS(Селектор текущего TSS + EXT);
FI;
NewSS = [База TSS + 8]; (* загружается 2 байта *)
NewESP = [База TSS + 4]; (* загружается 4 байта *)
Проверка селектора нового стекового сегмента NewSS и соответствующего ему дескриптора из LDT или GDT:
Селектор должен быть не нулевым, иначе #TS(EXT);
Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #TS(SS селектор + EXT);
RPL селектора должен быть равен нулю, иначе #TS(SS селектор + EXT);
DPL стекового сегмента должен быть равен нулю, иначе #TS(SS селектор + EXT);
Дескриптор должен иметь формат дескриптора сегмента данных, разрешенного для записи (W = 1), иначе #TS(SS селектор + EXT);
Сегмент должен присутствовать (P = 1), иначе #SS(SS селектор + EXT);
IF (32-битный шлюз)
THEN
Новый стек должен иметь место для 36 байт (40 байт, если есть код ошибки), иначе #SS(EXT)
Новый указатель инструкции должен попадать в пределы нового кодового сегмента, иначе #GP(EXT); (* указатель инструкции определяется значением поля OFFSET из дескриптора шлюза *)
TempEflags = EFLAGS;
EFLAGS.VM = 0; (* Процессор выходит из режима V86 для обработки прерывания в защищенном режиме *)
EFLAGS.NT = 0;
EFLAGS.TF = 0;
EFLAGS.RF = 0;
IF (Шлюз прерывания)
THEN EFLAGS.IF = 0;
FI;
TempSS = SS;
TempESP = ESP;
CPL = 0; (* Переключение на нулевой уровень привилегий *)
SS:ESP = TSS(NewSS:NewESP); (* Загрузить значения SS0 и ESP0 из TSS *)
Push(GS); (* расширяется до двух слов *)
Push(FS); (* расширяется до двух слов *)
Push(DS); (* расширяется до двух слов *)
Push(ES); (* расширяется до двух слов *)
GS = 0; (* Сегментные регистры обнуляются. Недопустимо последующее использование нулевых селекторов в защищенном режиме *)
FS = 0;
DS = 0;
ES = 0;
Push(TempSS); (* расширяется до двух слов *)
Push(TempESP);
Push(TempEflags);
Push(CS); (* расширяется до двух слов *)
Push(EIP);
Push(Код ошибки); (* Если присутствует, 4 байта *)
CS:EIP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 32-битного шлюза *)
ELSE (* 16-битный шлюз *)
Новый стек должен иметь место для 18 байт (20 байт, если есть код ошибки), иначе #SS(EXT)
(* сохранение в стеке 16-битных значений регистров происходит аналогично 32-битному шлюзу *)
(* Возврат из прерывания командой IRET обратно в режим V86 из 16-битного сегмента будет невозможен, так как флаг VM не сохранится в стеке и не восстановится из образа EFLAGS при возврате *)
FI;
(* Продолжение работы в защищенном режиме на нулевом уровне привилегий ... *)
END;
INT-TO-INTRA-PRIV: (* CR0.PE = 1, DPL = CPL или согласованный сегмент с DPL ≤ CPL *)
IF (32-битный шлюз)
THEN Новый стек должен иметь место для 12 байт (16 байт, если есть код ошибки), иначе #SS(EXT)
ELSE Новый стек должен иметь место для 6 байт (8 байт, если есть код ошибки), иначе #SS(EXT)
FI;
Новый указатель инструкции должен попадать в пределы нового кодового сегмента, иначе #GP(EXT);
IF (32-битный шлюз)
THEN
Push(EFLAGS);
Push(Длинный указатель на точку возврата); (* 3 слова дополняются до 4 *)
CS:EIP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 32-битного шлюза *)
Push(Код ошибки); (* Если присутствует, 4 байта *)
ELSE
Push(FLAGS);
Push(Длинный указатель на точку возврата); (* 2 слова *)
CS:IP = Gate(SELECTOR:OFFSET); (* Загрузить селектор:смещение из дескриптора 16-битного шлюза *)
Push(Код ошибки); (* Если присутствует, 2 байта *)
FI;
Загрузить дескриптор CS в скрытую часть регистра CS;
CS.RPL = CPL;
IF (Шлюз прерывания) THEN EFLAGS.IF = 0; FI;
EFLAGS.TF = 0;
EFLAGS.NT = 0;
EFLAGS.RF = 0;
(* Продолжение работы в защищенном режиме без изменения уровня привилегий ... *)
END;
TASK-GATE: (* CR0.PE = 1, шлюз задачи *)
Проверка селектора TSS из дескриптора шлюза задачи:
Селектор должен задавать GDT (бит TI = 0), иначе #GP(TSS селектор + EXT);
Индекс селектора должен попадать в пределы таблицы GDT, иначе #GP(TSS селектор + EXT);
Проверка соответствующего выбранному селектору дескриптора TSS:
Дескриптор TSS должен иметь тип свободного TSS (TYPE.B = 0), иначе #GP(TSS селектор + EXT);
TSS должен присутствовать (P = 1), иначе #NP(TSS селектор + EXT);
SWITCH-TASKS(С вложением) в TSS; (* здесь "С вложением" означает тот факт, что при инициализации контекста новой задачи будет установлен флаг EFLAGS.NT = 1, а в поле LINK ее сегмента TSS будет скопирован селектор TSS прерываемой (старой) задачи – см. Адресация и многозадачность: Средства поддержки мультизадачности *)
IF (Прерывание представляет собой особую ситуацию с кодом ошибки)
THEN
В стеке должно быть место для кода ошибки, иначе #SS(EXT);
Push(код ошибки);
FI;
Указатель инструкции EIP должен попадать в пределы сегмента CS, иначе #GP(EXT);
(* Загрузка контекста новой задачи сопровождается дополнительными проверками как это описано в разделе Адресация и многозадачность: Средства поддержки мультизадачности *)
(* Продолжение работы в контексте новой задачи ... *)
END;
Особые ситуации защищенного режима:
Представленный здесь перечень особых ситуаций характеризует поведение процессора не только при выполнении команды INT n, но и при поступлении любого внешнего прерывания или генерации особой ситуации. Бит EXT в коде ошибки используется для индикации внешнего по отношению к прерванной программе события (внешнего прерывания).
#GP(индекс прерывания * 8 + 2 + EXT), если:
- дескриптор, соответствующий обрабатываемому вектору (индексу) прерывания, не находится в пределах таблицы дескрипторов прерываний (IDT);
- дескриптор, соответствующий обрабатываемому вектору (индексу) прерывания, не является дескриптором шлюза ловушки, шлюза прерывания или шлюза задачи;
- имеет место программное прерывание или программная особая ситуация (то есть один из случаев: INT n, INT 3, INT01, BOUND или INTO) и при этом текущий уровень привилегий (CPL) задачи больше уровня привилегий (DPL) дескриптора шлюза из таблицы IDT —
#GP(0 + EXT), если указатель команды (OFFSET) в дескрипторе шлюза из таблицы IDT не попадает в пределы кодового сегмента.
#GP(код ошибки), если:
- селектор сегмента в соответствующем обрабатываемому вектору (индексу) прерывания дескрипторе шлюза ловушки, шлюза прерывания или шлюза задачи является нулевым селектором;
- индекс селектора сегмента из соответствующего прерыванию дескриптора шлюза ловушки или дескриптора шлюза прерывания не попадает в пределы соответствующей таблицы дескрипторов;
- индекс селектора TSS из соответствующего прерыванию дескриптора шлюза задачи не попадает в пределы глобальной таблицы дескрипторов (GDT);
- дескриптор нового кодового сегмента не является дескриптором сегмента кода;
- уровень привилегий дескриптора (DPL) нового согласованного кодового сегмента больше текущего уровеня привилегий (CPL) задачи –
- уровень привилегий дескриптора (DPL) нового несогласованного кодового сегмента не равен текущему уровеню привилегий (CPL) задачи –
- селектор TSS из соответствующего прерыванию дескриптора шлюза задачи указывает на локальную таблицу дескрипторов (LDT);
- дескриптор TSS новой задачи отмечен как занятый – TYPE.B ≠ 1.
#TS(код ошибки), если:
- адрес, по которому должно считываться новое значение для указателя стека (SS:eSP), выходит за пределы сегмента TSS;
- селектор нового сегмента стека является нулевым селектором;
- индекс селектора нового сегмента стека не попадает в пределы соответствующей таблицы дескрипторов;
- запрашиваемый уровень привилегий (RPL) селектора нового сегмента стека не равен уровню привилегий дескриптора (DPL) нового сегмента кода –
- уровень привилегий дескриптора (DPL) нового сегмента стека не равен уровню привилегий дескриптора (DPL) нового сегмента кода –
- новый стековый сегмент не является сегментом данных доступным для записи –
[TSS(SSx)].W) ≠ 1.
#NP(код ошибки), если:
- соответствующий прерыванию дескриптор шлюза ловушки, шлюза прерывания, шлюза задачи либо дескриптор TSS отмечен как неприсутствующий (бит P дескриптора сброшен);
- новый сегмент кода не присутствует (бит P дескриптора сегмента сброшен).
#SS(0 + EXT), если:
- при записи в стек (в том числе в новый стек, если имело место переключение стека) значений адреса возврата, указателя стека, флагов или кода ошибки просходит выход за допустимую границу стекового сегмента;
- новый сегмент стека не присутствует (бит P дескриптора сегмента сброшен).
#BP, если исполняется команда INT 3.
#OF, если исполняется команда INTO и флаг EFLAGS.OF = 1.
Intel386 … :
#PF(Код ошибки) при страничной ошибке.
#UD при использовании префикса LOCK.
#DB, если исполняется команда INT01.
Intel486 … :
#AC(EXT) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).
Особые ситуации режима реальной адресации:
Представленный здесь перечень особых ситуаций характеризует поведение процессора не только при выполнении команды INT n, но и при поступлении любого внешнего прерывания или генерации особой ситуации.
#GP, если вектор прерывания превышает предел таблицы векторов прерываний (IVT) (предел таблицы векторов прерываний может быть задан при программировании регистра IDTR).
#SS, при нарушении границ стекового сегмента во время сохранения данных в стеке.
#BP, если исполняется команда INT 3.
#OF, если исполняется команда INTO и флаг EFLAGS.OF = 1.
Intel386 … :
#UD при использовании префикса LOCK.
#DB, если исполняется команда INT01.
Особые ситуации режима V86:
Представленный здесь перечень особых ситуаций характеризует поведение процессора не только при выполнении команды INT n, но и при поступлении любого внешнего прерывания или генерации особой ситуации. Бит EXT в коде ошибки используется для индикации внешнего по отношению к прерванной программе события (внешнего прерывания).
#GP(индекс прерывания * 8 + 2 + EXT), если:
- дескриптор, соответствующий обрабатываемому вектору (индексу) прерывания, не находится в пределах таблицы дескрипторов прерываний (IDT);
- дескриптор, соответствующий обрабатываемому вектору (индексу) прерывания, не является дескриптором шлюза ловушки, шлюза прерывания или шлюза задачи.
- имеет место программное прерывание или программная особая ситуация (то есть один из случаев: INT n, INT 3, INT01, BOUND или INTO) и при этом уровень привилегий (DPL) дескриптора шлюза из таблицы IDT не равен 3 —
DPLшлюз ≠ 3.
#GP(0), если исполняется команда INT n когда текущий уровень привилегий ввода/вывода EFLAGS.IOPL < 3 (кроме режима EV86).
#GP(0 + EXT), если указатель команды (OFFSET) в дескрипторе шлюза из таблицы IDT не попадает в пределы кодового сегмента.
#GP(код ошибки), если:
- селектор сегмента в соответствующем обрабатываемому вектору (индексу) прерывания дескрипторе шлюза ловушки, шлюза прерывания или шлюза задачи является нулевым селектором;
- индекс селектора сегмента из соответствующего прерыванию дескриптора шлюза ловушки или дескриптора шлюза прерывания не попадает в пределы соответствующей таблицы дескрипторов;
- индекс селектора TSS из соответствующего прерыванию дескриптора шлюза задачи не попадает в пределы глобальной таблицы дескрипторов (GDT);
- дескриптор нового кодового сегмента не является дескриптором сегмента кода;
- уровень привилегий дескриптора (DPL) нового несогласованного кодового сегмента не равен нулю —
- селектор TSS из соответствующего прерыванию дескриптора шлюза задачи указывает на локальную таблицу дескрипторов (LDT);
- дескриптор TSS новой задачи отмечен как занятый – TYPE.B ≠ 1.
#TS(код ошибки), если:
- адрес, по которому должно считываться новое значение для указателя стека (SS0:ESP0), выходит за пределы сегмента TSS;
- селектор нового сегмента стека является нулевым селектором;
- индекс селектора нового сегмента стека не попадает в пределы соответствующей таблицы дескрипторов;
- запрашиваемый уровень привилегий (RPL) селектора нового сегмента стека не равен нулю —
TSS(SS0.RPL) ≠ 0;
- уровень привилегий дескриптора (DPL) нового сегмента стека не равен нулю —
[TSS(SS0)].DPL) ≠ 0;
- новый стековый сегмент не является сегментом данных доступным для записи –
[TSS(SS0)].W) ≠ 1.
#NP(код ошибки), если:
- соответствующий прерыванию дескриптор шлюза ловушки, шлюза прерывания, шлюза задачи либо дескриптор TSS отмечен как неприсутствующий (бит P дескриптора сброшен);
- новый сегмент кода не присутствует (бит P дескриптора сегмента сброшен).
#SS(0 + EXT), если:
- при записи в стек (в том числе в новый стек, после переключения стека) значений адреса возврата, указателя стека, сегментных регистров, флагов или кода ошибки просходит выход за допустимую границу стекового сегмента;
- новый сегмент стека не присутствует (бит P дескриптора сегмента сброшен).
#BP, если исполняется команда INT 3.
#OF, если исполняется команда INTO и флаг EFLAGS.OF = 1.
Intel386 … :
#PF(Код ошибки) при страничной ошибке.
#UD при использовании префикса LOCK.
#DB, если исполняется команда INT01.
Intel486 … :
#AC(EXT) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).
Pentium (в режиме EV86):
#GP(0), если исполняется команда INT n когда текущий уровень привилегий ввода/вывода EFLAGS.IOPL < 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 1.
Все права защищены © Алексей Ровдо, 1994-2023. Перепечатка возможна только по согласованию с владельцем авторских прав. admin@club155.ru