IRET/IRETD - 中斷返回

操作碼

指令

說明

CF

IRET

中斷返回(16 位運算元大小)

CF

IRETD

中斷返回(32 位運算元大小)

說明

將程序控制權從異常或中斷處理程式返回異常、外部中斷或軟體產生的中斷所中斷的程式或過程。這些指令也用於執行從巢狀任務的返回。(使用 CALL 指令啟動任務切換時,或是中斷、異常導致任務切換到中斷或異常程式時,都會建立巢狀任務)。請參閱“IA-32 英特爾(R) 體系結構軟件開發人員手冊”第 1 卷第 6 章中標題為“任務鏈接”的部分。

IRET 與 IRETD 是相同操作碼的助記符。IRETD 助記符(中斷返回雙字)用於從使用 32 位運算元大小的中斷返回;不過大多數彙編器對這兩種運算元大小都互換使用 IRET 助記符。

在“實地址模式”中,IRET 指令執行到中斷程式或過程的遠返回。在執行此操作的過程中,處理器從堆疊將返回指令指針、返回程式碼段選擇器以及 EFLAGS 映像分別彈入 EIP、CS 以及 EFLAGS 暫存器,然後恢復執行中斷的程式或過程。

在“保護模式”中,IRET 指令的操作取決於以下因素:EFLAGS 暫存器中 NT(巢狀任務)與 VM 標誌的設定,以及目前堆疊中儲存的 EFLAGS 映像中的 VM 標誌的設定。根據這些標誌的設定,處理器執行以下型別的中斷返回:

如果清除 NT 標誌(EFLAGS 暫存器),則 IRET 指令執行從中斷過程的遠返回,而不執行任務切換。返回到的程式碼段的特權級別必須小於或等於中斷處理例程的特權級別(如堆疊彈出的程式碼段選擇器的 PRL 欄位所示)。對於實地址模式中斷返回,IRET 指令從堆疊將返回指令指針、返回程式碼段選擇器以及 EFLAGS 映像分別彈入 EIP、CS 以及 EFLAGS 暫存器,然後恢復執行中斷的程式或過程。如果返回到另一個特權級別,則在恢復程式執行之前,IRET 指令還從堆疊彈出堆疊指針與 SS。如果返回到虛 8086 模式,處理器還從堆疊彈出數據段暫存器。

如果設定 NT 標誌,則 IRET 指令從巢狀任務(使用 CALL 指令、中斷或異常呼叫的任務)執行任務切換(返回)到呼叫或中斷的任務。執行 IRET 指令的任務的更新狀態儲存在它的 TSS 中。如果稍後重新進入該任務,則執行 IRET 指令後面的程式碼。

操作

IF PE 0
THEN
GOTO REAL-ADDRESS-MODE:;
ELSE
GOTO PROTECTED-MODE;
FI;

REAL-ADDRESS-MODE;
IF OperandSize 32
THEN
IF top 12 bytes of stack not within stack limits THEN #SS; FI;
IF instruction pointer not within code segment limits THEN #GP(0); FI;
EIP Pop();
CS Pop(); (* 32-bit pop, high-order 16 bits discarded *)
tempEFLAGS Pop();
EFLAGS (tempEFLAGS AND 257FD5H) OR (EFLAGS AND 1A0000H);
ELSE (* OperandSize 16 *)
IF top 6 bytes of stack are not within stack limits THEN #SS; FI;
IF instruction pointer not within code segment limits THEN #GP(0); FI;
EIP Pop();
EIP EIP AND 0000FFFFH;
CS Pop(); (* 16-bit pop *)
EFLAGS[15:0] Pop();
FI;
END;

PROTECTED-MODE:
IF VM 1 (* Virtual-8086 mode: PE=1, VM=1 *)
THEN
GOTO RETURN-FROM-VIRTUAL-8086-MODE; (* PE=1, VM=1 *)
FI;
IF NT 1
THEN
GOTO TASK-RETURN;( *PE=1, VM=0, NT=1 *)
FI;
IF OperandSize=32
THEN
IF top 12 bytes of stack not within stack limits
THEN #SS(0)
FI;
tempEIP Pop();
tempCS Pop();
tempEFLAGS Pop();
ELSE (* OperandSize 16 *)
IF top 6 bytes of stack are not within stack limits
THEN #SS(0);
FI;
tempEIP Pop();
tempCS Pop();
tempEFLAGS Pop();
tempEIP tempEIP AND FFFFH;
tempEFLAGS tempEFLAGS AND FFFFH;
FI;
IF tempEFLAGS(VM) 1 AND CPL=0
THEN
GOTO RETURN-TO-VIRTUAL-8086-MODE;
(* PE=1, VM=1 in EFLAGS image *)
ELSE
GOTO PROTECTED-MODE-RETURN;
(* PE=1, VM=0 in EFLAGS image *)
FI;

RETURN-FROM-VIRTUAL-8086-MODE:
(* Processor is in virtual-8086 mode when IRET is executed and stays in virtual-8086 mode *)
IF IOPL=3 (* Virtual mode: PE=1, VM=1, IOPL=3 *)
THEN IF OperandSize 32
THEN
IF top 12 bytes of stack not within stack limits THEN #SS(0); FI;
IF instruction pointer not within code segment limits THEN #GP(0); FI;
EIP Pop();
CS Pop(); (* 32-bit pop, high-order 16 bits discarded *)
EFLAGS Pop();
(*VM,IOPL,VIP,and VIF EFLAGS bits are not modified by pop *)
ELSE (* OperandSize 16 *)
IF top 6 bytes of stack are not within stack limits THEN #SS(0); FI;
IF instruction pointer not within code segment limits THEN #GP(0); FI;
EIP Pop();
EIP EIP AND 0000FFFFH;
CS Pop(); (* 16-bit pop *)
EFLAGS[15:0] Pop(); (* IOPL in EFLAGS is not modified by pop *)
FI;
ELSE
#GP(0); (* trap to virtual-8086 monitor: PE=1, VM=1, IOPL<3 *)
FI;
END;

RETURN-TO-VIRTUAL-8086-MODE:
(* Interrupted procedure was in virtual-8086 mode: PE=1, VM=1 in flags image *)
IF top 24 bytes of stack are not within stack segment limits
THEN #SS(0);
FI;
IF instruction pointer not within code segment limits
THEN #GP(0);
FI;
CS tempCS;
EIP tempEIP;
EFLAGS tempEFLAGS
TempESP Pop();
TempSS Pop();
ES Pop(); (* pop 2 words; throw away high-order word *)
DS Pop(); (* pop 2 words; throw away high-order word *)
FS Pop(); (* pop 2 words; throw away high-order word *)
GS Pop(); (* pop 2 words; throw away high-order word *)
SS:ESP TempSS:TempESP;
(* Resume execution in Virtual-8086 mode *)
END;

TASK-RETURN: (* PE=1, VM=1, NT=1 *)
Read segment selector in link field of current TSS;
IF local/global bit is set to local
OR index not within GDT limits
THEN #GP(TSS selector);
FI;
Access TSS for task specified in link field of current TSS;
IF TSS descriptor type is not TSS or if the TSS is marked not busy
THEN #GP(TSS selector);
FI;
IF TSS not present
THEN #NP(TSS selector);
FI;
SWITCH-TASKS (without nesting) to TSS specified in link field of current TSS;
Mark the task just abandoned as NOT BUSY;
IF EIP is not within code segment limit
THEN #GP(0);
FI;
END;

PROTECTED-MODE-RETURN: (* PE=1, VM=0 in flags image *)
IF return code segment selector is null THEN GP(0); FI;
IF return code segment selector addrsses descriptor beyond descriptor table limit
THEN GP(selector; FI;
Read segment descriptor pointed to by the return code segment selector
IF return code segment descriptor is not a code segment THEN #GP(selector); FI;
IF return code segment selector RPL < CPL THEN #GP(selector); FI;
IF return code segment descriptor is conforming
AND return code segment DPL > return code segment selector RPL
THEN #GP(selector); FI;
IF return code segment descriptor is not present THEN #NP(selector); FI:
IF return code segment selector RPL > CPL
THEN GOTO RETURN-OUTER-PRIVILEGE-LEVEL;
ELSE GOTO RETURN-TO-SAME-PRIVILEGE-LEVEL
FI;
END;

RETURN-TO-SAME-PRIVILEGE-LEVEL: (* PE=1, VM=0 in flags image, RPL=CPL *)
IF EIP is not within code segment limits THEN #GP(0); FI;
EIP tempEIP;
CS tempCS; (* segment descriptor information also loaded *)
EFLAGS (CF, PF, AF, ZF, SF, TF, DF, OF, NT) tempEFLAGS;
IF OperandSize=32
THEN
EFLAGS(RF, AC, ID) tempEFLAGS;
FI;
IF CPL IOPL
THEN
EFLAGS(IF) tempEFLAGS;
FI;
IF CPL 0
THEN
EFLAGS(IOPL) tempEFLAGS;
IF OperandSize=32
THEN EFLAGS(VM, VIF, VIP) tempEFLAGS;
FI;
FI;
END;

RETURN-TO-OUTER-PRIVILGE-LEVEL:
IF OperandSize=32
THEN
IF top 8 bytes on stack are not within limits THEN #SS(0); FI;
ELSE (* OperandSize=16 *)
IF top 4 bytes on stack are not within limits THEN #SS(0); FI;
FI;
Read return segment selector;
IF stack segment selector is null THEN #GP(0); FI;
IF return stack segment selector index is not within its descriptor table limits
THEN #GP(SSselector); FI;
Read segment descriptor pointed to by return segment selector;
IF stack segment selector RPL RPL of the return code segment selector
IF stack segment selector RPL RPL of the return code segment selector
OR the stack segment descriptor does not indicate a writable data segment;
OR stack segment DPL RPL of the return code segment selector
THEN #GP(SS selector);
FI;
IF stack segment is not present THEN #SS(SS selector); FI;
IF tempEIP is not within code segment limit THEN #GP(0); FI;
EIP tempEIP;
CS tempCS;
EFLAGS (CF, PF, AF, ZF, SF, TF, DF, OF, NT) tempEFLAGS;
IF OperandSize=32
THEN
EFLAGS(RF, AC, ID) tempEFLAGS;
FI;
IF CPL IOPL
THEN
EFLAGS(IF) tempEFLAGS;
FI;
IF CPL 0
THEN
EFLAGS(IOPL) tempEFLAGS;
IF OperandSize=32
THEN EFLAGS(VM, VIF, VIP) tempEFLAGS;
FI;
FI;
CPL RPL of the return code segment selector;
FOR each of segment register (ES, FS, GS, and DS)
DO;
IF segment register points to data or non-conforming code segment
AND CPL > segment descriptor DPL (* stored in hidden part of segment register *)
THEN (* segment register invalid *)
SegmentSelector 0; (* null segment selector *)
FI;
OD;
END:

影響的標誌

根據處理器的操作模式,可能會修改 EFLAGS 暫存器中的所有標誌與欄位。如果執行從巢狀任務到前一項任務的返回,則根據前一項任務的 TSS 儲存的 EFLAGS 映像修改 EFLAGS 暫存器。

保護模式異常

#GP(0) - 如果返回程式碼或堆疊段選擇器為空。如果返回指令指針超出返回程式碼段限制。

#GP(選擇器) - 如果段選擇器索引超出描述符表格限制。如果返回程式碼段選擇器 RPL 大於 CPL。如果相容程式碼段的 DPL 大於返回程式碼段選擇器的 RPL。如果非相容程式碼段的 DPL 不等於程式碼段選擇器的 RPL。如果堆疊段描述符的 DPL 不等於返回程式碼段選擇器的 RPL。如果堆疊段不是可寫的數據段。如果堆疊段選擇器 RPL 不等於返回程式碼段選擇器的 RPL。如果程式碼段的段描述符未指明它是程式碼段。如果 TSS 的段選擇器將自己的區域性/全域性位設定為區域性。如果 TSS 段描述符指出 TSS 忙或不可用。

#SS(0) - 如果堆疊的棧頂位元組超出堆疊限制。

#NP(選擇器) - 如果返回程式碼或堆疊段不存在。

#PF(錯誤程式碼) - 如果發生頁錯誤。

#AC(0) - 如果在 CPL 是 3 且啟用對齊檢查的情況下發生未對齊的記憶體引用。

實地址模式異常

#GP - 如果返回指令指針超出返回程式碼段限制。

#SS - 如果堆疊的棧頂位元組超出堆疊限制。

虛 8086 模式異常

#GP(0) - 如果返回指令指針超出返回程式碼段限制。如果 IOPL 不等於 3。

#PF(錯誤程式碼) - 如果發生頁錯誤。

#SS(0) - 如果堆疊的棧頂位元組超出堆疊限制。

#AC(0) - 如果在啟用對齊檢查的情況下進行未對齊的記憶體引用。