Краткий ответ:
Если прерывание находится на более высоком уровне привилегий, SS и ESP помещаются в стек, каждый из которых занимает 4 байта. Для того же уровня этот шаг пропущен.
Затем EFLAGS, CS и EIP помещаются в стек, каждый из которых также использует 4 байта. Таким образом, у вас, скорее всего, будет 20 байт в стеке.
Существует множество сложных исключений и правил для шлюзов задач, сбоев, переключений контекста и т. Д., Поэтому длинный ответ в псевдокоде из x86 / x64 Справочник по набору инструкций :
(* The following operational description applies not only to the INT n and INTO instructions, but also to external interrupts, nonmaskable interrupts (NMIs), and exceptions. Some of these events push onto the stack an error code. *)
(* The operational description specifies numerous checks whose failure may result in delivery of a nested exception. In these cases, the original event is not delivered. *)
(* The operational description specifies the error code delivered by any nested exception. In some cases, the error code is specified with a pseudofunction error_code(num, idt, ext), where idt and ext are bit values. The pseudofunction produces an error code as follows: *)
(* (1) if idt is 0, the error code is (num & FCH) | ext; *)
(* (2) if idt is 1, the error code is (num << 3) | 2 | ext. *)
(* In many cases, the pseudofunction error_code is invoked with a pseudovariable EXT. The value of EXT depends on the nature of the event whose delivery encountered a nested exception: if that event is a software interrupt, EXT is 0;otherwise, EXT is 1. *)
IF PE = 0
GOTO REAL-ADDRESS-MODE;
ELSE
IF PE = 1
IF (VM = 1 and IOPL < 3 AND INT n)
#GP(0); (* Bit 0 of error code is 0 because INT n *)
ELSE
(* Protected mode, IA-32e mode, or virtual-8086 mode interrupt *)
IF (IA32_EFER.LMA = 0)
(* Protected mode, or virtual-8086 mode interrupt *)
GOTO PROTECTED-MODE;
ELSE
(* IA-32e mode interrupt *)
GOTO IA-32e-MODE;
FI;
FI;
FI;
FI;
REAL-ADDRESS-MODE:
IF ((vector_number << 2) + 3) is not within IDT limit
#GP;
FI;
IF stack not large enough for a 6-byte return information
#SS;
FI;
Push (EFLAGS[15:0]);
IF = 0; (* Clear interrupt flag *)
TF = 0; (* Clear trap flag *)
AC = 0; (* Clear AC flag *)
Push(CS);
Push(IP);
(* No error codes are pushed in real-address mode *)
CS = IDT(Descriptor (vector_number << 2), selector));
EIP = IDT(Descriptor (vector_number << 2), offset)); (* 16 bit offset AND 0000FFFFH *)
END;
PROTECTED-MODE:
IF ((vector_number << 3) + 7) is not within IDT limits or selected IDT descriptor is not an interrupt-, trap-, or task-gate type
#GP(error_code(vector_number, 1, EXT));
FI;
(* idt operand to error_code set because vector is used *)
IF software interrupt (* Generated by INT n, INT3, or INTO *)
IF gate DPL < CPL (* PE = 1, DPL < CPL, software interrupt *)
#GP(error_code(vector_number, 1, 0));
FI;
(* idt operand to error_code set because vector is used *)
(* ext operand to error_code is 0 because INT n, INT3, or INTO *)
FI;
IF gate not present
#NP(error_code(vector_number, 1, EXT));
FI;
(* idt operand to error_code set because vector is used *)
IF task gate (* Specified in the selected interrupt table descriptor *)
GOTO TASK-GATE;
ELSE
GOTO TRAP-OR-INTERRUPT-GATE; (* PE = 1, trap/interrupt gate *)
FI;
END;
IA-32e-MODE:
IF INTO and CS.L = 1 (64-bit mode)
#UD;
FI;
IF ((vector_number << 4) + 15) is not in IDT limits or selected IDT descriptor is not an interrupt-, or trap-gate type
#GP(error_code(vector_number, 1, EXT));
(* idt operand to error_code set because vector is used *)
FI;
IF software interrupt (* Generated by INT n, INT 3, or INTO *)
IF gate DPL < CPL (* PE = 1, DPL < CPL, software interrupt *)
#GP(error_code(vector_number, 1, 0)); (* idt operand to error_code set because vector is used *)
(* ext operand to error_code is 0 because INT n, INT3, or INTO *)
FI;
FI;
IF gate not present
#NP(error_code(vector_number, 1, EXT));
(* idt operand to error_code set because vector is used *)
FI;
GOTO TRAP-OR-INTERRUPT-GATE; (* Trap/interrupt gate *)
END;
TASK-GATE: (* PE = 1, task gate *)
Read TSS selector in task gate (IDT descriptor);
IF local/global bit is set to local or index not within GDT limits
#GP(error_code(TSS selector, 0, EXT));
FI; (* idt operand to error_code is 0 because selector is used *)
Access TSS descriptor in GDT;
IF TSS descriptor specifies that the TSS is busy (low-order 5 bits set to 00001)
#GP(TSS selector, 0, EXT));
FI; (* idt operand to error_code is 0 because selector is used *)
IF TSS not present
#NP(TSS selector, 0, EXT));
FI; (* idt operand to error_code is 0 because selector is used *)
SWITCH-TASKS (with nesting) to TSS;
IF interrupt caused by fault with error code
IF stack limit does not allow push of error code
#SS(EXT);
FI;
Push(error code);
FI;
IF EIP not within code segment limit
#GP(EXT);
FI;
END;
TRAP-OR-INTERRUPT-GATE:
Read new code-segment selector for trap or interrupt gate (IDT descriptor);
IF new code-segment selector is NULL
#GP(EXT);
FI; (* Error code contains NULL selector *)
IF new code-segment selector is not within its descriptor table limits
#GP(error_code(new code-segment selector, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
Read descriptor referenced by new code-segment selector;
IF descriptor does not indicate a code segment or new code-segment DPL > CPL
#GP(error_code(new code-segment selector, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
IF new code-segment descriptor is not present,
#NP(error_code(new code-segment selector, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
IF new code segment is non-conforming with DPL < CPL
IF VM = 0
GOTO INTER-PRIVILEGE-LEVEL-INTERRUPT; (* PE = 1, VM = 0, interrupt or trap gate, nonconforming code segment, DPL < CPL *)
ELSE
(* VM = 1 *)
IF new code-segment DPL != 0
#GP(error_code(new code-segment selector, 0, EXT)); (* idt operand to error_code is 0 because selector is used *)
GOTO INTERRUPT-FROM-VIRTUAL-8086-MODE;
FI;
(* PE = 1, interrupt or trap gate, DPL < CPL, VM = 1 *)
FI;
ELSE
(* PE = 1, interrupt or trap gate, DPL >= CPL *)
IF VM = 1
#GP(error_code(new code-segment selector, 0, EXT)); (* idt operand to error_code is 0 because selector is used *)
FI;
IF new code segment is conforming or new code-segment DPL = CPL
GOTO INTRA-PRIVILEGE-LEVEL-INTERRUPT;
ELSE
(* PE = 1, interrupt or trap gate, nonconforming code segment, DPL > CPL *)
#GP(error_code(new code-segment selector, 0, EXT)); (* idt operand to error_code is 0 because selector is used *)
FI;
FI;
END;
INTER-PRIVILEGE-LEVEL-INTERRUPT: (* PE = 1, interrupt or trap gate, non-conforming code segment, DPL < CPL *)
IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *)
(* Identify stack-segment selector for new privilege level in current TSS *)
IF current TSS is 32-bit
TSSstackAddress = (new code-segment DPL << 3) + 4;
IF (TSSstackAddress + 5) > current TSS limit
#TS(error_code(current TSS selector, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
NewSS = 2 bytes loaded from (TSS base + TSSstackAddress + 4);
NewESP = 4 bytes loaded from (TSS base + TSSstackAddress);
ELSE
(* current TSS is 16-bit *)
TSSstackAddress = (new code-segment DPL << 2) + 2
IF (TSSstackAddress + 3) > current TSS limit
#TS(error_code(current TSS selector, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
NewSS = 2 bytes loaded from (TSS base + TSSstackAddress + 2);
NewESP = 2 bytes loaded from (TSS base + TSSstackAddress);
FI;
IF NewSS is NULL
#TS(EXT);
FI;
IF NewSS index is not within its descriptor-table limits or NewSS RPL != new code-segment DPL
#TS(error_code(NewSS, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
Read new stack-segment descriptor for NewSS in GDT or LDT;
IF new stack-segment DPL != new code-segment DPL or new stack-segment Type does not indicate writable data segment
#TS(error_code(NewSS, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
IF NewSS is not present
#SS(error_code(NewSS, 0, EXT));
FI; (* idt operand to error_code is 0 because selector is used *)
ELSE
(* IA-32e mode *)
IF IDT-gate IST = 0
TSSstackAddress = (new code-segment DPL << 3) + 4;
ELSE
TSSstackAddress = (IDT gate IST << 3) + 28;
FI;
IF (TSSstackAddress + 7) > current TSS limit
#TS(error_code(current TSS selector, 0, EXT);
FI;
(* idt operand to error_code is 0 because selector is used *)
NewRSP = 8 bytes loaded from (current TSS base + TSSstackAddress);
NewSS = new code-segment DPL; (* NULL selector with RPL = new CPL *)
FI;
IF IDT gate is 32-bit
IF new stack does not have room for 24 bytes (error code pushed) or 20 bytes (no error code pushed)
#SS(error_code(NewSS, 0, EXT));
(* idt operand to error_code is 0 because selector is used *)
FI
ELSE
IF IDT gate is 16-bit
IF new stack does not have room for 12 bytes (error code pushed) or 10 bytes (no error code pushed);
#SS(error_code(NewSS, 0, EXT));
FI; (* idt operand to error_code is 0 because selector is used *)
ELSE
(* 64-bit IDT gate *)
IF StackAddress is non-canonical
#SS(EXT);
FI; (* Error code contains NULL selector *)
FI;
FI;
IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *)
IF instruction pointer from IDT gate is not within new code-segment limits
#GP(EXT);
FI; (* Error code contains NULL selector *)
ESP = NewESP;
SS = NewSS; (* Segment descriptor information also loaded *)
ELSE
(* IA-32e mode *)
IF instruction pointer from IDT gate contains a non-canonical address
#GP(EXT);
FI; (* Error code contains NULL selector *)
RSP = NewRSP & FFFFFFFFFFFFFFF0H;
SS = NewSS;
FI;
IF IDT gate is 32-bit
CS:EIP = Gate(CS:EIP); (* Segment descriptor information also loaded *)
ELSE
IF IDT gate 16-bit
CS:IP = Gate(CS:IP); (* Segment descriptor information also loaded *)
ELSE
(* 64-bit IDT gate *)
CS:RIP = Gate(CS:RIP); (* Segment descriptor information also loaded *)
FI;
FI;
IF IDT gate is 32-bit
Push(far pointer to old stack); (* Old SS and ESP, 3 words padded to 4 *)
Push(EFLAGS);
Push(far pointer to return instruction); (* Old CS and EIP, 3 words padded to 4 *)
Push(ErrorCode); (* If needed, 4 bytes *)
ELSE
IF IDT gate 16-bit
Push(far pointer to old stack); (* Old SS and SP, 2 words *)
Push(EFLAGS(15-0]);
Push(far pointer to return instruction); (* Old CS and IP, 2 words *)
Push(ErrorCode); (* If needed, 2 bytes *)
ELSE
(* 64-bit IDT gate *)
Push(far pointer to old stack); (* Old SS and SP, each an 8-byte push *)
Push(RFLAGS); (* 8-byte push *)
Push(far pointer to return instruction); (* Old CS and RIP, each an 8-byte push *)
Push(ErrorCode); (* If needed, 8-bytes *)
FI;
FI;
CPL = new code-segment DPL;
CS(RPL) = CPL;
IF IDT gate is interrupt gate
IF = 0 (* Interrupt flag set to 0, interrupts disabled *);
FI;
TF = 0;
VM = 0;
RF = 0;
NT = 0;
END;
INTERRUPT-FROM-VIRTUAL-8086-MODE: (* Identify stack-segment selector for privilege level 0 in current TSS *)
IF current TSS is 32-bit
IF TSS limit < 9
#TS(error_code(current TSS selector, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
NewSS = 2 bytes loaded from (current TSS base + 8);
NewESP = 4 bytes loaded from (current TSS base + 4);
ELSE
(* current TSS is 16-bit *)
IF TSS limit < 5
#TS(error_code(current TSS selector, 0, EXT));
FI; (* idt operand to error_code is 0 because selector is used *)
NewSS = 2 bytes loaded from (current TSS base + 4);
NewESP = 2 bytes loaded from (current TSS base + 2);
FI;
IF NewSS is NULL
#TS(EXT);
FI; (* Error code contains NULL selector *)
IF NewSS index is not within its descriptor table limits or NewSS RPL != 0
#TS(error_code(NewSS, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
Read new stack-segment descriptor for NewSS in GDT or LDT;
IF new stack-segment DPL != 0 or stack segment does not indicate writable data segment
#TS(error_code(NewSS, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
IF new stack segment not present
#SS(error_code(NewSS, 0, EXT));
FI; (* idt operand to error_code is 0 because selector is used *)
IF IDT gate is 32-bit
IF new stack does not have room for 40 bytes (error code pushed) or 36 bytes (no error code pushed)
#SS(error_code(NewSS, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
ELSE
(* IDT gate is 16-bit *)
IF new stack does not have room for 20 bytes (error code pushed) or 18 bytes (no error code pushed)
#SS(error_code(NewSS, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
FI;
IF instruction pointer from IDT gate is not within new code-segment limits
#GP(EXT);
FI; (* Error code contains NULL selector *)
tempEFLAGS = EFLAGS;
VM = 0;
TF = 0;
RF = 0;
NT = 0;
IF service through interrupt gate
IF = 0;
FI;
TempSS = SS;
TempESP = ESP;
SS = NewSS;
ESP = NewESP;
(* Following pushes are 16 bits for 16-bit IDT gates and 32 bits for 32-bit IDT gates;Segment selector pushes in 32-bit mode are padded to two words *)
Push(GS);
Push(FS);
Push(DS);
Push(ES);
Push(TempSS);
Push(TempESP);
Push(TempEFlags);
Push(CS);
Push(EIP);
GS = 0; (* Segment registers made NULL, invalid for use in protected mode *)
FS = 0;
DS = 0;
ES = 0;
CS:IP = Gate(CS); (* Segment descriptor information also loaded *)
IF OperandSize = 32
EIP = Gate(instruction pointer);
ELSE
(* OperandSize is 16 *)
EIP = Gate(instruction pointer) AND 0000FFFFH;
FI;
(* Start execution of new routine in Protected Mode *)
END;
INTRA-PRIVILEGE-LEVEL-INTERRUPT:
(* PE = 1, DPL = CPL or conforming segment *)
IF IA32_EFER.LMA = 1 (* IA-32e mode *)
IF IDT-descriptor IST != 0
TSSstackAddress = (IDT-descriptor IST << 3) + 28;
IF (TSSstackAddress + 7) > TSS limit
#TS(error_code(current TSS selector, 0, EXT));
FI;
(* idt operand to error_code is 0 because selector is used *)
NewRSP = 8 bytes loaded from (current TSS base + TSSstackAddress);
FI;
IF 32-bit gate (* implies IA32_EFER.LMA = 0 *)
IF current stack does not have room for 16 bytes (error code pushed) or 12 bytes (no error code pushed)
#SS(EXT);
FI; (* Error code contains NULL selector *)
ELSE
IF 16-bit gate (* implies IA32_EFER.LMA = 0 *)
IF current stack does not have room for 8 bytes (error code pushed) or 6 bytes (no error code pushed)
#SS(EXT);
FI; (* Error code contains NULL selector *)
ELSE
(* IA32_EFER.LMA = 1, 64-bit gate *)
IF NewRSP contains a non-canonical address
#SS(EXT); (* Error code contains NULL selector *)
FI;
FI;
FI;
FI;
IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *)
IF instruction pointer from IDT gate is not within new code-segment limit
#GP(EXT);
FI; (* Error code contains NULL selector *)
ELSE
IF instruction pointer from IDT gate contains a non-canonical address
#GP(EXT);
FI; (* Error code contains NULL selector *)
RSP = NewRSP & FFFFFFFFFFFFFFF0H;
FI;
IF IDT gate is 32-bit (* implies IA32_EFER.LMA = 0 *)
Push (EFLAGS);
Push (far pointer to return instruction); (* 3 words padded to 4 *)
CS:EIP = Gate(CS:EIP); (* Segment descriptor information also loaded *)
Push (ErrorCode); (* If any *)
ELSE
IF IDT gate is 16-bit (* implies IA32_EFER.LMA = 0 *)
Push (FLAGS);
Push (far pointer to return location); (* 2 words *)
CS:IP = Gate(CS:IP); (* Segment descriptor information also loaded *)
Push (ErrorCode); (* If any *)
ELSE
(* IA32_EFER.LMA = 1, 64-bit gate *)
Push(far pointer to old stack); (* Old SS and SP, each an 8-byte push *)
Push(RFLAGS); (* 8-byte push *)
Push(far pointer to return instruction); (* Old CS and RIP, each an 8-byte push *)
Push(ErrorCode); (* If needed, 8 bytes *)
CS:RIP = GATE(CS:RIP);
(* Segment descriptor information also loaded *)
FI;
FI;
CS(RPL) = CPL;
IF IDT gate is interrupt gate
IF = 0;
FI;
(* Interrupt flag set to 0;interrupts disabled *)
TF = 0;
NT = 0;
VM = 0;
RF = 0;
END;