Система команд x86 |
|
Программирование - Архитектура и система команд микропроцессоров x86 |
IRET
Влияние команды на флаги и форматы команды:
* |
* |
* |
* |
* |
* |
* |
* |
* |
CF |
IRET |
Возврат из прерывания при 16-битном размере операнда |
8086 |
iret |
CF |
IRETD |
Возврат из прерывания при 32-битном размере операнда |
Intel386 |
iretd |
Описание:
Команда IRET/IRETD предназначена для возврата управления из процедуры обработчика прерывания или особой ситуации в прерванную программу. Эта команда также используется для возврата из подпрограмм, которые оформлены как вложенные задачи и вызываются командой CALL через шлюз задачи.
Мнемоники IRET и IRETD имеют один код операции. Конкретная выполняемая команда определяется атрибутом размера операнда. При 16-битном размере операнда исполняется IRET, при 32-битном — IRETD. Некоторые ассемблеры могут автоматически вставлять соответствующий префикс размера операнда перед кодом операции при трансляции мнемоник IRET и IRETD. Другие же ассемблеры воспринимают эти мнемоники одинаково и программируют только код операции. Работа команды в этом случае определяется текущей установкой размера операнда для кодового сегмента (бит D дескриптора сегмента).
В режиме реальной адресации (CR0.PE = 0) команда IRET/IRETD последовательно выбирает из стека указатель команд (E)IP, регистр CS и регистр флагов (E)FLAGS и продолжает выполнение прерванной программы или процедуры.
В защищенном режиме (CR0.PE = 1) действие команды IRET/IRETD зависит от текущего значения флага вложенной задачи (EFLAGS.NT), флага виртуального режима (EFLAGS.VM), текущего и возвращаемого уровней привилегий (CPL), текущего уровня привилегий ввода/вывода (EFLAGS.IOPL), флага активации расширений виртуального режима (CR4.VME), флага отложенного виртуального прерывания (EFLAGS.VIP) и битов VM, IF, TF в образе регистра флагов в стеке. Различают следующие типы возврата из прерывания:
- Возврат без изменения уровня привилегий;
- Возврат на уровень с меньшими привилегиями;
- Возврат в режим V86 (из защищенного режима);
- Возврат в режиме V86;
- Возврат в режиме EV86 (при активированных виртуальных прерываниях – CR4.VME = 1);
- Возврат из вложенной задачи (переключение задач).
Если флаг вложенной задачи установлен (EFLAGS.NT = 1), то команда IRET/IRETD производит действие обратное действиям команды CALL или INT, которая вызвала переключение задачи. Обновленное состояние задачи при выполнении команды IRET/IRETD сохраняется в сегменте состояния задачи. При этом команда IRET/IRETD меняет статус занятости в дескрипторе TSS покидаемой задачи на «свободно» (бит B = 0 в поле TYPE дескриптора), а также сбрасывает бит NT в сохраняемом в соответствующем сегменте TSS образе регистра (E)FLAGS. Состояние занятости в дескрипторе TSS новой задачи, в которую передается управление, командой IRET/IRETD не изменяется, так как оно должно было установиться в «занято» (бит B = 1 в поле TYPE дескриптора) в момент передачи управления во вложенную задачу командой CALL или INT. Программы любого уровня привилегий могут устанавливать флаг EFLAGS.NT = 1. Это однако не позволяет нарушать правила защиты, так как целостность и последовательность обращения к вложенным задачам обеспечивается через поле обратной связи (LINK) сегмента TSS. Команда IRET/IRETD передает управление только в ту задачу, которая (точнее, селектор дескриптора TSS которой) указана в поле LINK сегмента TSS покидаемой задачи.
Если флаг вложенной задачи сброшен (EFLAGS.NT = 0), то команда IRET/IRETD возвращает управление из процедуры прерывания без переключения задачи. Код, которому возвращается управление в защищенном режиме, должен быть равно или менее привилегирован, чем процедура прерывания (что задается битами RPL селектора CS, получаемого из стека).
При возврате из процедуры обработчика прерывания или особой ситуации в защищенном режиме процессор, как и в режиме реальной адресации, автоматически считывает из стека сохраненные ранее значения регистров (E)IP, CS и (E)FLAGS. Если код назначения менее привилегирован, то команда IRET/IRETD также получает из стека указатель стека (E)SP и селектор стекового сегмента SS. Когда из стека загружается новое значение регистра флагов (E)FLAGS, то изменение флага разрешения прерываний IF возможно только при выполнении условия CPL ≤ IOPL (текущие значения до перегрузки). Сами же биты уровня привилегий ввода/вывода (IOPL) изменяются только если текущий уровень привилегий CPL = 0.
Если происходит возврат в программу режима V86, то из стека также загружаются значения сегментных регистров ES, DS, FS, GS.
В режиме V86 доступен только возврат без выхода из этого режима. То есть флаг виртуального режима EFLAGS.VM, если он был установлен, уже нельзя сбросить командой IRETD, загрузив новое значение из образа в стеке. Это, однако, возможно, если речь идет о возврате из вложенной задачи, которая была ранее вызвана командами CALL или INT с использованием шлюза задачи. Также при использовании команды IRET/IRETD в режиме V86 (когда виртуальные прерывания не активированы – CR4.VME = 0) невозможно модифицировать биты RF, IOPL, VIP и VIF регистра EFLAGS. Если при вызове команды IRET/IRETD в режиме V86 текущий уровень привилегий ввода/вывода EFLAGS.IOPL меньше текущего уровня привилегий задачи (то есть EFLAGS.IOPL < 3), то генерируется ошибка общей защиты (#GP) и управление передается в обработчик этой ошибки. Такое поведение называется IOPL-чувствительностью и в режиме V86 характерно также для команд CLI, STI, POPF/POPFD, PUSHF/PUSHFD, INT n.
В режиме EV86 (при активированных виртуальных прерываниях – CR4.VME = 1) возможно использование команды IRET (16-битный размер операнда), когда соблюдается требование EFLAGS.IOPL < 3. В таком случае команда не позволяет изменять флаг IF или устанавливать флаг TF регистра FLAGS, но вместо загрузки флага прерываний IF его образ копируется во флаг виртуальных прерываний VIF в старшем слове регистра EFLAGS, а при попытке установки флага трассировки TF генерируется ошибка общей защиты #GP(0). Для обеспечения обработки отложенных виртуальных прерываний ошибка общей защиты #GP(0) генерируется также тогда, когда команда IRET пытается загрузить из стека образ с установленным значением бита IF при уже установленном флаге отложенного виртуального прерывания EFLAGS.VIP.
Если команда IRETD загружает из образа в стеке флаг возобновления EFLAGS.RF = 1, то он не сбрасывается сразу по завершении исполнения команды IRETD, а остается установленным до окончания выполнения последующей команды, позволяя таким образом ее перезапуск без генерации особой ситуации отладки (DB#). Тут также следует учитывать, что процессор при генерации особой ситуации отладки (DB#) (если это не точка останова по команде) или если прерывание возникает во время исполнения строковой команды автоматически записывает в стек образ EFLAGS со значением бита RF = 1. То есть обработчик особой ситуации и не должен ничего предпринимать, перед возвратом управления в прерванную программу для перезапуска соответствующей команды. В случае же точки останова по команде, процессор записывает в стек образ EFLAGS со значением бита RF = 0. В этом случае обработчик особой ситуации должен перед возвратом управления либо вообще деактивировать точку останова, либо изменить значение в стеке, установив в образе регистра флагов RF = 1, иначе перезапуск команды вызовет повторную генерацию особой особой ситуации отладки (DB#).
Немаскируемые прерывания (NMI) не блокируются флагом EFLAGS.IF, но такая блокировка возникает сразу после генерации немаскируемого прерывания. Блокировка действует вплоть до первого исполнения команды IRET/IRETD, которая ее и снимает.
Операция:
IF CR0.PE = 0
THEN GOTO REAL_ADDRESS_MODE;
ELSE GOTO PROTECTED_MODE;
FI;
REAL_ADDRESS_MODE:
IF OperandSize = 32 (* Команда IRETD *)
THEN
IF ( 12 байт из вершины стека выходят за его пределы )
THEN #SS;
FI;
tempDWORD = SS:[ESP] (* 4 байта из вершины стека *)
IF ( tempDWORD[31:16] ≠ 0 ) THEN #GP; FI;
EIP = Pop(); (* 32-битная выборка *)
CS = Pop(); (* 32-битная выборка, старшие 16 бит отбрасываются *)
tempEFLAGS = Pop();
EFLAGS = (tempEFLAGS AND 0x257FD5h) OR (EFLAGS AND 0x1A0000h); (* флаги VIP, VIF, VM регистра EFLAGS не изменяются *)
ELSE (* OperandSize = 16 - команда IRET *)
IF ( 6 байт из вершины стека выходят за его пределы )
THEN #SS;
FI;
EIP = Pop() AND 0x0000FFFFh; (* 32-битная выборка, старшие 16 бит сбрасываются *)
CS = Pop(); (* 16-битная выборка *)
FLAGS = Pop(); (* 16-битная выборка, флаги в старшем слове регистра EFLAGS не изменяются *)
FI;
END;
PROTECTED_MODE:
IF EFLAGS.VM = 1 (* Виртуальный режим: CR0.PE = 1, EFLAGS.VM = 1 *)
THEN GOTO RETURN_FROM_V86; (* CR0.PE = 1, EFLAGS.VM = 1 *)
ELSE (* EFLAGS.VM = 0 *)
IF EFLAGS.NT = 1
THEN GOTO TASK_RETURN; (* CR0.PE = 1, EFLAGS.VM = 0, EFLAGS.NT = 1 *)
ELSE (* EFLAGS.NT = 0 *)
IF OperandSize = 32
THEN
IF ( 12 байт из вершины стека выходят за его пределы ) THEN #SS(0); FI;
tempEIP = Pop();
tempCS = Pop();
tempEFLAGS = Pop();
ELSE (* OperandSize = 16 *)
IF ( 6 байт из вершины стека выходят за его пределы ) THEN #SS(0); FI;
tempEIP = Pop() AND 0x0000FFFFh; (* 16-битная выборка, старшее слово сбрасывается *)
tempCS = Pop(); (* 16-битная выборка *)
tempEFLAGS[15:0] = Pop(); (* 16-битная выборка *)
FI;
IF tempEFLAGS.VM = 1 AND CPL = 0
THEN GOTO RETURN_TO_V86; (* VM = 1 в образе флагов в стеке *)
ELSE GOTO PROTECTED_MODE_RETURN; (* VM = 0 в образе флагов в стеке *)
FI;
FI;
FI;
END;
RETURN_FROM_V86:
IF EFLAGS.IOPL = 3 (* Виртуальный режим: CR0.PE = 1, EFLAGS.VM = 1, EFLAGS.IOPL = 3 *)
THEN
IF OperandSize = 16
THEN
IF ( 6 байт из вершины стека выходят за его пределы ) THEN #SS(0); FI;
IF ( Указатель команды из вершины стека не попадает в пределы кодового сегмента ) THEN #GP(0); FI;
EIP = Pop() AND 0x0000FFFFh; (* 16-битная выборка, старшее слово сбрасывается *)
CS = Pop(); (* 16-битная выборка *)
tempFLAGS = Pop() AND NOT 0xB02Ah; (* 16-битная выборка, поле IOPL маскируется при выборке нового значения FLAGS из стека *)
FLAGS = ( FLAGS AND 0x3002h ) OR tempFLAGS; (* Поле IOPL регистра FLAGS не модифицируется *)
ELSE (* OperandSize = 32 *)
IF ( 12 байт из вершины стека выходят за его пределы ) THEN #SS(0); FI;
IF ( Указатель команды из вершины стека не попадает в пределы кодового сегмента ) THEN #GP(0); FI;
EIP = Pop();
CS = Pop(); (* 32-битная выборка, старшие 16 бит отбрасываются *)
tempEFLAGS = Pop() AND NOT 0x1BB02Ah; (* 32-битная выборка, биты RF, VM, IOPL, VIP и VIF маскируются при выборке нового значения EFLAGS из стека *)
EFLAGS = ( EFLAGS AND 0x1B3002h ) OR tempEFLAGS; (* Биты RF, VM, IOPL, VIP и VIF регистра EFLAGS не модифицируются *)
FI;
ELSE (* EFLAGS.IOPL < 3 *)
IF ( (CR4.VME = 0) or (OperandSize = 32) )
THEN #GP(0); (* Ловушка монитора V86 *)
ELSE (* CR4.VME = 1 и OperandSize = 16 *)
IF ( 6 байт из вершины стека выходят за его пределы ) THEN #SS(0); FI;
IF ( Указатель команды из вершины стека не попадает в пределы кодового сегмента ) THEN #GP(0); FI;
IF ( [SP+4].TF = 1 ) (* Образ регистра FLAGS в стеке содержит флаг TF = 1 *)
THEN #GP(0); (* Ловушка монитора V86 *)
ELSE
IF ( EFLAGS.VIP = 1 and [SP+4].IF = 1 ) (* Попытка установки флага IF когда уже установлен флаг VIP *)
THEN #GP(0); (* Ловушка монитора V86 *)
ELSE
EIP = Pop() AND 0x0000FFFFh; (* 16-битная выборка, старшее слово сбрасывается *)
CS = Pop(); (* 16-битная выборка *)
tempFLAGS = Pop(); (* 16-битная выборка *)
EFLAGS.VIF = tempFLAGS.IF;
tempFLAGS = tempFLAGS AND NOT 0xB22Ah; (* биты IOPL и IF регистра FLAGS маскируются *)
FLAGS = ( FLAGS AND 0x3302h ) OR tempFLAGS; (* Биты IOPL, IF, TF регистра FLAGS не модифицируются *)
FI;
FI;
FI;
FI;
(* Продолжение работы в виртуальном-8086 режиме ... *)
END;
RETURN_TO_V86: (* Прерывание было обработано в защищенном режиме: CR0.PE = 1, VM = 0 *)
(* В образе флагов в стеке VM = 1 - производится возврат из защищенного режима в режим V86 *)
IF ( 24 байт из вершины стека выходят за его пределы ) THEN #SS(0); FI;
IF (Указатель команды из вершины стека не попадает в пределы кодового сегмента) THEN #GP(0); FI;
CS = tempCS;
EIP = tempEIP;
EFLAGS = tempEFLAGS;
TempESP = Pop();
TempSS = Pop(); (* Выбирается 2 слова, старшее слово отбрасывается *)
ES = Pop(); (* Выбирается 2 слова, старшее слово отбрасывается *)
DS = Pop(); (* Выбирается 2 слова, старшее слово отбрасывается *)
FS = Pop(); (* Выбирается 2 слова, старшее слово отбрасывается *)
GS = Pop(); (* Выбирается 2 слова, старшее слово отбрасывается *)
SS:ESP = TempSS:TempESP;
CPL = 3;
(* Продолжение работы в виртуальном-8086 режиме ... *)
END;
TASK_RETURN: (* CR0.PE = 1, EFLAGS.VM = 0, EFLAGS.NT = 1 *)
Проверка селектора обратной связи LINK в TSS текущей задачи:
Селектор должен указывать на GDT (бит TI = 0), иначе #TS(новый TSS селектор);
Индекс селектора должен попадать в пределы таблицы GDT, иначе #TS(новый TSS селектор);
Проверка дескриптора TSS новой задачи в таблице GDT, на который указывает селектор обратной связи LINK в TSS текущей задачи:
Дескриптор должен иметь тип "дескриптор TSS" и признак занятого (TYPE.B = 1), иначе #TS(новый TSS селектор);
Предел в поле Limit дескриптора TSS должен быть больше 107 (для 32-битного TSS) или 43 (для 16-битного TSS), иначе #TS(новый TSS селектор);
TSS должен присутствовать (P = 1), иначе #NP(новый TSS селектор);
SWITCH-TASKS(Без вложеннности) в TSS, заданный селектором обратной связи в поле LINK текущего TSS; (* здесь "Без вложенности" означает тот факт, что поле NT в образе EFLAGS, сохраняемом в сегменте TSS покидаемой задачи, будет очищено NT = 0, а для новой задачи будет взято значение EFLAGS.NT из образа в сегменте TSS без изменения – см. Адресация и многозадачность: Средства поддержки мультизадачности *)
Пометить покинутую задачу, как не занятую (TYPE.B = 0);
Указатель инструкции в EIP должен попадать в пределы сегмента CS, иначе #GP(0);
(* Загрузка контекста новой задачи сопровождается дополнительными проверками как это описано в разделе Адресация и многозадачность: Средства поддержки мультизадачности *)
(* Продолжение работы в контексте новой задачи ... *)
END;
PROTECTED_MODE_RETURN: (* Прерывание было обработано в защищенном режиме: CR0.PE = 1, VM = 0 *)
(* tempEFLAGS.VM = 0 - возврат в защищенный режим *)
Проверка возвращаемого селектора кодового сегмента tempCS и соответствующего дескриптора в таблице дескрипторов:
Селектор должен быть не нулевым, иначе #GP(0);
Селектор должен задавать индекс дескриптора попадающий в пределы соответствующей таблицы дескрипторов, иначе #GP(tempCS);
Задаваемый селектором дескриптор должен быть дескриптором кодового сегмента, иначе #GP(tempCS);
tempCS.RPL ≥ CPL, иначе #GP(tempCS);
Если задаваемый селектором дескриптор описывает согласованный кодовый сегмент, то DPL дескриптора ≤ tempCS.RPL, иначе #GP(tempCS);
IF (tempCS.RPL > CPL)
THEN GOTO RETURN-OUTER-PRIV-LEVEL;
ELSE GOTO RETURN-SAME-PRIV-LEVEL;
FI;
END;
RETURN-SAME-PRIV-LEVEL: (* возврат в защищенный режим: tempEFLAGS.VM = 0, tempCS.RPL = CPL *)
IF tempEIP не попадает в пределы кодового сегмента THEN #GP(0); FI;
EIP = tempEIP;
CS = tempCS;
EFLAGS(CF, PF, AF, ZF, SF, TF, DF, OF, NT) = tempEFLAGS(CF, PF, AF, ZF, SF, TF, DF, OF, NT);
IF OperandSize = 32
THEN EFLAGS(RF, AC, ID) = tempEFLAGS(RF, AC, ID);
FI;
IF CPL ≤ EFLAGS.IOPL
THEN EFLAGS.IF = tempEFLAGS.IF;
FI;
IF CPL = 0 (* tempEFLAGS.VM = 0 *)
THEN
EFLAGS.IOPL = tempEFLAGS.IOPL;
IF OperandSize = 32
THEN EFLAGS(VM, VIF, VIP) = tempEFLAGS(VM, VIF, VIP);
FI;
FI;
(* Продолжение работы в защищенном режиме без изменения текущего уровня привилегий и стекового сегмента ... *)
END;
RETURN-OUTER-PRIV-LEVEL: (* возврат в защищенный режим: tempEFLAGS.VM = 0, tempCS.RPL > CPL *)
IF OperandSize = 32
THEN
В пределы стека должны попадать верхние 8 байт, иначе #SS(0);
tempESP = Pop();
tempSS = Pop(); (* Выбирается 2 слова, старшее слово отбрасывается *)
ELSE
В пределы стека должны попадать верхние 4 байт, иначе #SS(0);
tempESP = Pop() AND 0x0000FFFFh; (* 16-битная выборка, старшее слово сбрасывается *)
tempSS = Pop(); (* 16-битная выборка *)
FI;
Проверка возвращаемого селектора tempSS стекового сегмента и соответствующего дескриптора стека:
Селектор должен быть не нулевым, иначе #GP(0);
Индекс селектора должен попадать в пределы таблицы дескрипторов, иначе #GP(tempSS);
tempSS.RPL = tempCS.RPL, иначе #GP(tempSS);
AR байт дескриптора должен задавать разрешенный для записи сегмент данных, иначе #GP(tempSS);
tempSS.DPL = tempCS.RPL, иначе #GP(tempSS);
Сегмент должен присутствовать, иначе #SS(tempSS);
tempEIP должен попадать в пределы кодового сегмента, иначе #GP(0);
EIP = tempEIP;
CS = tempCS;
EFLAGS(CF, PF, AF, ZF, SF, TF, DF, OF, NT) = tempEFLAGS(CF, PF, AF, ZF, SF, TF, DF, OF, NT);
IF OperandSize = 32
THEN
EFLAGS(RF, AC, ID) = tempEFLAGS(RF, AC, ID);
FI;
SS:ESP = TempSS:TempESP;
IF CPL ≤ IOPL THEN EFLAGS.IF = tempEFLAGS.IF; FI;
IF CPL = 0
THEN
EFLAGS.IOPL = tempEFLAGS.IOPL;
IF OperandSize = 32
THEN EFLAGS(VM, VIF, VIP) = tempEFLAGS(VM, VIF, VIP);
FI;
FI;
CPL = tempCS.RPL;
DO (Для каждого из регистров ES, FS, GS и DS)
IF (Селектор в регистре задает сегмент данных или несогласованный кодовый сегмент, при этом CPL > DPL дескриптора сегмента)
THEN (* Значение регистра недопустимо для текущего уровня привилегий *)
Селектор = 0; (* Обнулить регистр *)
FI;
OD;
(* Продолжение работы в защищенном режиме на новом уровне привилегий с новым стековым сегментом ... *)
END;
Особые ситуации защищенного режима:
#GP(0), если возвращается нулевой селектор кодового или стекового сегмента.
#GP(0), если возвращаемый указатель команды EIP не попадает в пределы кодового сегмента.
#GP(селектор), если:
- индекс возвращаемого селектора сегмента не попадает в пределы соответствующей таблицы дескрипторов;
- запрашиваемый уровень привилегий (RPL) нового кодового сегмента меньше текущего уровня привилегий (CPL) – Stack(CS.RPL) < CPL;
- уровень привилегий дескриптора (DPL) текущего согласованного кодового сегмента больше запрашиваемого уровня привилегий (RPL) нового кодового сегмента – [CS].DPL > Stack(CS.RPL);
- уровень привилегий дескриптора (DPL) текущего несогласованного кодового сегмента не равен запрашиваемому уровню привилегий (RPL) нового кодового сегмента – [CS].DPL ≠ Stack(CS.RPL);
- уровень привилегий дескриптора (DPL) нового стекового сегмента не равен запрашиваемому уровню привилегий (RPL) нового кодового сегмента – [Stack(SS)].DPL) ≠ Stack(CS.RPL);
- новый стековый сегмент не является сегментом данных доступным для записи – [Stack(SS)].W) ≠ 1;
- запрашиваемый уровень привилегий (RPL) нового стекового сегмента не равен запрашиваемому уровню привилегий (RPL) нового кодового сегмента – Stack(SS.RPL) ≠ Stack(CS.RPL);
- дескриптор нового кодового сегмента не является дескриптором сегмента кода;
#TS(селектор нового TSS), если:
- селектор дескриптора TSS указывает на локальную таблицу дескрипторов (LDT);
- дескриптор TSS возвращаемой задачи не отмечен как занятый – TYPE.B ≠ 1;
#SS(0) если при извлечении данных из стека просходит выход за допустимую верхнюю границу стекового сегмента.
#NP(селектор) если возвращаемый кодовый или стековый сегмент, либо дескриптор TSS возвращаемой задачи, отмечен как неприсутствующий (бит P соответствующего дескриптора сегмента сброшен).
Intel386 … :
#PF(Код ошибки) при страничной ошибке.
#UD при использовании префикса LOCK.
Intel486 … :
#AC(0) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).
Особые ситуации режима реальной адресации:
#GP, если возвращаемый указатель команды не попадает в допустимые для реального режима пределы кодового сегмента;
#SS, если при извлечении данных из стека просходит выход за допустимую для реального режима верхнюю границу стекового сегмента.
Особые ситуации режима V86:
#GP(0), если возвращаемый указатель команды EIP не попадает в пределы кодового сегмента.
#GP(0), если уровень привилегий ввода/вывода не равен 3 (EFLAGS.IOPL ≠ 3) (кроме режима EV86).
#SS(0), если при извлечении данных из стека просходит выход за допустимую верхнюю границу стекового сегмента.
#UD при использовании префикса LOCK.
#PF(Код ошибки) при страничной ошибке.
Intel486 … :
#AC(0) при невыровненной ссылке в память, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC = 1, CPL = 3).
Pentium … (в режиме EV86):
#GP(0), если атрибут размера операнда не равен 16.
#GP(0), если загружаемый образ регистра FLAGS в стеке содержит установленный флаг трассировки TF = 1.
#GP(0) при попытке загрузки из образа в стеке установленного флага разрешения прерываний EFLAGS.IF когда уже установлен флаг отложенного виртуального прерывания EFLAGS.VIP = 1.
Замечание:
Команды IRET/IRETD относятся к так называемым сериализующим командам (Serializing instructions), которые не могут исполняться различными оптимизирующими механизмами вне порядка их следования (out-of-order). Более того, процессор ультимативно очищает все свои буферы параллельного и опережающего исполнения команд в тот момент, когда заканчивается исполнение сериализующей команды. По факту это может оказывать заметное негативное влияние на скорость исполнения кода в целом (особенно, если сериализующих команд в нем много). Поэтому использование сериализующих команд в критических к скорости исполнения блоках кода рекомендуется минимизировать.
Программа-монитор режима V86 предусматривает, что привилегированные команды, нарушающие модель защиты при текущем уровне привилегий ввода/вывода EFLAGS.IOPL < 3, обрабатываются через вызов особой ситуации общей защиты (#GP). Но если флаг TF будет установлен командой IRET в режиме EV86, то вслед за ней по логике должна будет генерироваться особая ситуация отладки (#DB). Чтобы предотвратить несанкционированный монитором V86 переход к обработчику особой ситуации отладки (#DB), процессор проверяет образ, извлекаемый из стека, и при попытке установки флага TF (в режиме EV86 когда EFLAGS.IOPL < 3) генерирует особую ситуации общей защиты (#GP). По всей видимости, эта «фича» существует с самого момента появления режима виртуальных прерываний в процессоре Pentium, хотя о ней и не было информации в официальной документации. Вместе с тем, замечено, что описанное изменение флага TF командой IRET в некоторых моделях процессоров может отрабатываться иначе и не приводить к генерации особой ситуации общей защиты (#GP) (это, похоже, является ошибкой или еще одной «фичей», которая также никогда официально не описывалась). Аналогичное ограничение (и ошибка) существует и для команды POPF.
В некоторых моделях процессоров возможна ошибочная генерация особой ситуации #GP вместо #SS, когда команда IRET/IRETD нарушает границу стекового сегмента.
Все права защищены © Алексей Ровдо, 1994-2023. Перепечатка возможна только по согласованию с владельцем авторских прав. admin@club155.ru