IA32 CPU, защищенный режим: какие данные сохраняются в стек при возникновении прерывания? - PullRequest
1 голос
/ 21 февраля 2012

Я хотел бы знать размер в байтах, который каждый фрагмент данных использует в стеке.Как уровень привилегий влияет на ответ?

1 Ответ

2 голосов
/ 21 февраля 2012

Краткий ответ:

Если прерывание находится на более высоком уровне привилегий, 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;
...