;如果操作数类型是16位的, ;则将32位临时偏移量的高16位清零 IF OperandSize = 16 THEN tempEIP ← tempEIP AND 0000FFFFH ;FI;
;如果临时EIP数值超过代码段的界限 ;并且长模式未激活、目标模式为兼容模式这两个条件有一个成立 ;引发异常 IF (IA32_EFER.LMA = 0 or target mode = Compatibility mode) and tempEIP outside code segment limit THEN #GP(0) ; FI
;如果临时EIP数值不规范 ;引发异常 IF tempEIP is non-canonical THEN #GP(0) ; FI;
IF far call and (PE = 1 and VM = 0) (* Protected mode or IA-32e Mode, not virtual-8086 mode*) THEN IF segment selector in target operand NULL THEN #GP(0); FI;
IF segment selector index not within descriptor table limits THEN #GP(new code segment selector); FI;
Read type and access rights of selected segment descriptor;
IF IA32_EFER.LMA = 0 THEN IF segment type is not a conforming or nonconforming code segment, call gate, task gate, or TSS THEN #GP(segment selector); FI; ELSE IF segment type is not a conforming or nonconforming code segment or 64-bit call gate, THEN #GP(segment selector); FI; FI;
Depending on type and access rights: GO TO CONFORMING-CODE-SEGMENT; GO TO NONCONFORMING-CODE-SEGMENT; GO TO CALL-GATE; GO TO TASK-GATE; GO TO TASK-STATE-SEGMENT; FI;
CONFORMING-CODE-SEGMENT: IF L bit = 1 and D bit = 1 and IA32_EFER.LMA = 1 THEN GP(new code segment selector); FI;
IF DPL > CPL THEN #GP(new code segment selector); FI;
IF segment not present THEN #NP(new code segment selector); FI;
IF stack not large enough for return address THEN #SS(0); FI;
tempEIP ← DEST(Offset);
IF OperandSize = 16 THEN tempEIP ← tempEIP AND 0000FFFFH; FI; (* Clear upper 16 bits *)
IF (EFER.LMA = 0 or target mode = Compatibility mode) and (tempEIP outside new code segment limit) THEN #GP(0); FI;
IF tempEIP is non-canonical THEN #GP(0); FI;
IF OperandSize = 32 THEN Push(CS); (* Padded with 16 high-order bits *) Push(EIP); CS ← DEST(CodeSegmentSelector); (* Segment descriptor information also loaded *) CS(RPL) ← CPL; EIP ← tempEIP; ELSE IF OperandSize = 16 THEN Push(CS); Push(IP); CS ← DEST(CodeSegmentSelector); (* Segment descriptor information also loaded *) CS(RPL) ← CPL; EIP ← tempEIP; ELSE (* OperandSize = 64 *) Push(CS); (* Padded with 48 high-order bits *) Push(RIP); CS ← DEST(CodeSegmentSelector); (* Segment descriptor information also loaded *) CS(RPL) ← CPL; RIP ← tempEIP; FI; FI; END;
NONCONFORMING-CODE-SEGMENT: IF L-Bit = 1 and D-BIT = 1 and IA32_EFER.LMA = 1 THEN GP(new code segment selector); FI;
IF (RPL > CPL) or (DPL ≠ CPL) THEN #GP(new code segment selector); FI;
IF segment not present THEN #NP(new code segment selector); FI;
IF stack not large enough for return address THEN #SS(0); FI;
tempEIP ← DEST(Offset);
IF OperandSize = 16 THEN tempEIP ← tempEIP AND 0000FFFFH; FI; (* Clear upper 16 bits *)
IF (EFER.LMA = 0 or target mode = Compatibility mode) and (tempEIP outside new code segment limit) THEN #GP(0); FI;
IF tempEIP is non-canonical THEN #GP(0); FI;
IF OperandSize = 32 THEN Push(CS); (* Padded with 16 high-order bits *) Push(EIP); CS ← DEST(CodeSegmentSelector); (* Segment descriptor information also loaded *) CS(RPL) ← CPL; EIP ← tempEIP; ELSE IF OperandSize = 16 THEN Push(CS); Push(IP); CS ← DEST(CodeSegmentSelector); (* Segment descriptor information also loaded *) CS(RPL) ← CPL; EIP ← tempEIP; ELSE (* OperandSize = 64 *) Push(CS); (* Padded with 48 high-order bits *) Push(RIP); CS ← DEST(CodeSegmentSelector); (* Segment descriptor information also loaded *) CS(RPL) ← CPL; RIP ← tempEIP; FI; FI; END; 上面是CALL跳转到一致代码和非一致代码的逻辑说明。同样都是用当前特权级CPL刷新了CS寄存器中最低2位RPL的值。
下面是CALL通过调用门提权的逻辑过程,这个就是真正提升了当前特权级。 CALL-GATE: IF call gate (DPL < CPL) or (RPL > DPL) THEN #GP(call-gate selector); FI;
IF call gate not present THEN #NP(call-gate selector); FI;
IF call-gate code-segment selector is NULL THEN #GP(0); FI;
IF call-gate code-segment selector index is outside descriptor table limits THEN #GP(call-gate code-segment selector); FI;
Read call-gate code-segment descriptor;
IF call-gate code-segment descriptor does not indicate a code segment or call-gate code-segment descriptor DPL > CPL THEN #GP(call-gate code-segment selector); FI;
IF IA32_EFER.LMA = 1 AND (call-gate code-segment descriptor is not a 64-bit code segment or call-gate code-segment descriptor has both L-bit and D-bit set) THEN #GP(call-gate code-segment selector); FI;
IF call-gate code segment not present THEN #NP(call-gate code-segment selector); FI;
IF call-gate code segment is non-conforming and DPL < CPL THEN go to MORE-PRIVILEGE; ELSE go to SAME-PRIVILEGE; FI; END;
MORE-PRIVILEGE: IF current TSS is 32-bit THEN TSSstackAddress ← (new code-segment DPL*8) + 4; IF (TSSstackAddress + 5) > current TSS limit THEN #TS(current TSS selector); FI; NewSS ← 2 bytes loaded from (TSS base + TSSstackAddress + 4); NewESP ← 4 bytes loaded from (TSS base + TSSstackAddress); ELSE IF current TSS is 16-bit THEN TSSstackAddress ← (new code-segment DPL*4) + 2 IF (TSSstackAddress + 3) > current TSS limit THEN #TS(current TSS selector); FI; NewSS ← 2 bytes loaded from (TSS base + TSSstackAddress + 2); NewESP ← 2 bytes loaded from (TSS base + TSSstackAddress); ELSE (* current TSS is 64-bit *) TSSstackAddress ← (new code-segment DPL*8) + 4; IF (TSSstackAddress + 7) > current TSS limit THEN #TS(current TSS selector); FI; NewSS ← new code-segment DPL; (* NULL selector with RPL = new CPL *) NewRSP ← 8 bytes loaded from (current TSS base + TSSstackAddress); FI; FI; IF IA32_EFER.LMA = 0 and NewSS is NULL THEN #TS(NewSS); FI; Read new code-segment descriptor and new stack-segment descriptor; IF IA32_EFER.LMA = 0 and (NewSS RPL ≠ new code-segment DPL or new stack-segment DPL ≠ new code-segment DPL or new stack segment is not a writable data segment) THEN #TS(NewSS); FI IF IA32_EFER.LMA = 0 and new stack segment not present THEN #SS(NewSS); FI; IF CallGateSize = 32 THEN IF new stack does not have room for parameters plus 16 bytes THEN #SS(NewSS); FI; IF CallGate(InstructionPointer) not within new code-segment limit THEN #GP(0); FI; SS ← newSS; (* Segment descriptor information also loaded *) ESP ← newESP; CS:EIP ← CallGate(CS:InstructionPointer); (* Segment descriptor information also loaded *) Push(oldSS:oldESP); (* From calling procedure *) temp ← parameter count from call gate, masked to 5 bits; Push(parameters from calling procedure’s stack, temp) Push(oldCS:oldEIP); (* Return address to calling procedure *) ELSE IF CallGateSize = 16 THEN IF new stack does not have room for parameters plus 8 bytes THEN #SS(NewSS); FI; IF (CallGate(InstructionPointer) AND FFFFH) not in new code-segment limit THEN #GP(0); FI; SS ← newSS; (* Segment descriptor information also loaded *) ESP ← newESP; CS:IP ← CallGate(CS:InstructionPointer); (* Segment descriptor information also loaded *) Push(oldSS:oldESP); (* From calling procedure *) temp ← parameter count from call gate, masked to 5 bits; Push(parameters from calling procedure’s stack, temp) Push(oldCS:oldEIP); (* Return address to calling procedure *) ELSE (* CallGateSize = 64 *) IF pushing 32 bytes on the stack would use a non-canonical address THEN #SS(NewSS); FI; IF (CallGate(InstructionPointer) is non-canonical) THEN #GP(0); FI; SS ← NewSS; (* NewSS is NULL) RSP ← NewESP; CS:IP ← CallGate(CS:InstructionPointer); (* Segment descriptor information also loaded *) Push(oldSS:oldESP); (* From calling procedure *) Push(oldCS:oldEIP); (* Return address to calling procedure *) FI; FI; CPL ← CodeSegment(DPL) CS(RPL) ← CPL END; 上面代码倒数第3行,处理器将目标代码段描述符的DPL给了CPL,并用这个CPL刷新了CS的最低2位RPL的值,也就是说,当前特权级真正到了目标代码特权级。这是所有的跳转里面唯一提升了当前特权级的。 当然,CALL通过调用门同样也可以不提升特权级,下面就是CALL通过调用门,但不提升当前特权级。 SAME-PRIVILEGE: IF CallGateSize = 32 THEN IF stack does not have room for 8 bytes THEN #SS(0); FI; IF CallGate(InstructionPointer) not within code segment limit THEN #GP(0); FI; CS:EIP ← CallGate(CS:EIP) (* Segment descriptor information also loaded *) Push(oldCS:oldEIP); (* Return address to calling procedure *) ELSE If CallGateSize = 16 THEN IF stack does not have room for 4 bytes THEN #SS(0); FI; IF CallGate(InstructionPointer) not within code segment limit THEN #GP(0); FI; CS:IP ← CallGate(CS:instruction pointer); (* Segment descriptor information also loaded *) Push(oldCS:oldIP); (* Return address to calling procedure *) ELSE (* CallGateSize = 64) IF pushing 16 bytes on the stack touches non-canonical addresses THEN #SS(0); FI; IF RIP non-canonical THEN #GP(0); FI; CS:IP ← CallGate(CS:instruction pointer); (* Segment descriptor information also loaded *) Push(oldCS:oldIP); (* Return address to calling procedure *) FI; FI; CS(RPL) ← CPL END; 倒数第2行,处理器还是用当前特权级(也就是旧特权级,因为此时还处于跳转过程处理,并未到目标代码段)刷新了CS中的RPL值。