EENTER — Enters an Enclave

Opcode/Instruction Op/En 64/32 bit Mode Support CPUID Feature Flag Description
EAX = 02H ENCLU[EENTER] IR V/V SGX1 This leaf function is used to enter an enclave.

Instruction Operand Encoding

Op/En EAX RBX RCX
IR EENTER (In) Content of RBX.CSSA (Out) Address of a TCS (In) Address of AEP (In) Address of IP following EENTER (Out)

Description

The ENCLU[EENTER] instruction transfers execution to an enclave. At the end of the instruction, the logical processor is executing in enclave mode at the RIP computed as EnclaveBase + TCS.OENTRY. If the target address is not within the CS segment (32-bit) or is not canonical (64-bit), a #GP(0) results.

EENTER Memory Parameter Semantics

TCS
Enclave access

EENTER is a serializing instruction. The instruction faults if any of the following occurs:

Address in RBX is not properly aligned. Any TCS.FLAGS’s must-be-zero bit is not zero.
TCS pointed to by RBX is not valid or available or locked. Current 32/64 mode does not match the enclave mode in SECS.ATTRIBUTES.MODE64.
The SECS is in use. Either of TCS-specified FS and GS segment is not a subsets of the current DS segment.
Any one of DS, ES, CS, SS is not zero. If XSAVE available, CR4.OSXSAVE = 0, but SECS.ATTRIBUTES.XFRM ≠ 3.
CR4.OSFXSR ≠ 1. If CR4.OSXSAVE = 1, SECS.ATTRIBUTES.XFRM is not a subset of XCR0.
If SECS.ATTRIBUTES.AEXNOTIFY ≠ TCS.FLAGS.AEXNOTIFY and TCS.FLAGS.DBGOPTIN = 0.

The following operations are performed by EENTER:

Concurrency Restrictions

Leaf Parameter Base Concurrency Restrictions
On Conflict
EENTER EENTER TCS [DS:RBX] Shared EENTER TCS [DS:RBX] TCS [DS:RBX]
Table 38-62. Base Concurrency Restrictions of EENTER
Leaf Parameter Additional Concurrency Restrictions
vs. EACCEPT, EACCEPTCOPY, vs. EADD, EEXTEND, EINIT vs. ETRACK, ETRACKC Access vs. ETRACK, ETRACKC Access On Conflict Access vs. ETRACK, ETRACKC Access On Conflict EMODPE, EMODPR, EMODT vs. EADD, EEXTEND, EINIT vs. EADD, EEXTEND, EINIT vs. ETRACK, ETRACKC vs. ETRACK, ETRACKC
Access On Conflict Access On Conflict Access Access On Conflict Access On Conflict
EENTER TCS [DS:RBX] Concurrent Concurrent Concurrent
Table 38-63. Additional Concurrency Restrictions of EENTER

Operation

Temp Variables in EENTER Operational Flow

Name Type Size (Bits) Description
TMP_FSBASE Effective Address 32/64 Proposed base address for FS segment.
TMP_GSBASE Effective Address 32/64 Proposed base address for FS segment.
TMP_FSLIMIT Effective Address 32/64 Highest legal address in proposed FS segment.
TMP_GSLIMIT Effective Address 32/64 Highest legal address in proposed GS segment.
TMP_XSIZE integer 64 Size of XSAVE area based on SECS.ATTRIBUTES.XFRM.
TMP_SSA_PAGE Effective Address 32/64 Pointer used to iterate over the SSA pages in the current frame.
TMP_GPR Effective Address 32/64 Address of the GPR area within the current SSA frame.

TMP_MODE64 := ((IA32_EFER.LMA = 1) && (CS.L = 1));

(* Make sure DS is usable, expand up *)

IF (TMP_MODE64 = 0 and (DS not usable or ( ( DS[S] = 1) and (DS[bit 11] = 0) and DS[bit 10] = 1) ) )

THEN #GP(0); FI;

(* Check that CS, SS, DS, ES.base is 0 *)

IF (TMP_MODE64 = 0)

THEN

IF(CS.base ≠ 0 or DS.base ≠ 0) #GP(0); FI;

IF(ES usable and ES.base ≠ 0) #GP(0); FI;

IF(SS usable and SS.base ≠ 0) #GP(0); FI;

IF(SS usable and SS.B = 0) #GP(0); FI;

FI;

IF (DS:RBX is not 4KByte Aligned)

THEN #GP(0); FI;

IF (DS:RBX does not resolve within an EPC)

THEN #PF(DS:RBX); FI;

(* Check AEP is canonical*)

IF (TMP_MODE64 = 1 and (CS:RCX is not canonical) )

THEN #GP(0); FI;

(* Check concurrency of TCS operation*)

IF (Other Intel SGX instructions are operating on TCS)

THEN #GP(0); FI;

(* TCS verification *)

IF (EPCM(DS:RBX).VALID = 0)

THEN #PF(DS:RBX); FI;

IF (EPCM(DS:RBX).BLOCKED = 1)

THEN #PF(DS:RBX); FI;

IF ( (EPCM(DS:RBX).ENCLAVEADDRESS ≠ DS:RBX) or (EPCM(DS:RBX).PT ≠ PT_TCS) )

THEN #PF(DS:RBX); FI;

IF ((EPCM(DS:RBX).PENDING = 1) or (EPCM(DS:RBX).MODIFIED = 1))

THEN #PF(DS:RBX); FI;

IF ( (DS:RBX).OSSA is not 4KByte Aligned)

THEN #GP(0); FI;

(* Check proposed FS and GS *)

IF ( ( (DS:RBX).OFSBASE is not 4KByte Aligned) or ( (DS:RBX).OGSBASE is not 4KByte Aligned) )

THEN #GP(0); FI;

(* Get the SECS for the enclave in which the TCS resides *)

TMP_SECS := Address of SECS for TCS;

(* Ensure that the FLAGS field in the TCS does not have any reserved bits set *)

IF ( ( (DS:RBX).FLAGS & FFFFFFFFFFFFFFFCH) ≠ 0)

THEN #GP(0); FI;

(* SECS must exist and enclave must have previously been EINITted *)

IF (the enclave is not already initialized)

THEN #GP(0); FI;

(* make sure the logical processor’s operating mode matches the enclave *)

IF ( (TMP_MODE64 ≠ TMP_SECS.ATTRIBUTES.MODE64BIT) )

THEN #GP(0); FI;

IF (CR4.OSFXSR = 0)

THEN #GP(0); FI;

(* Check for legal values of SECS.ATTRIBUTES.XFRM *)

IF (CR4.OSXSAVE = 0)

THEN

IF (TMP_SECS.ATTRIBUTES.XFRM ≠ 03H) THEN #GP(0); FI;

ELSE

IF ( (TMP_SECS.ATTRIBUTES.XFRM & XCR0) ≠ TMP_SECS.ATTRIBUES.XFRM) THEN #GP(0); FI;

FI;

IF ((DS:RBX).CSSA.FLAGS.DBGOPTIN = 0) and (DS:RBX).CSSA.FLAGS.AEXNOTIFY ≠ TMP_SECS.ATTRIBUTES.AEXNOTIFY))

THEN #GP(0); FI;

(* Make sure the SSA contains at least one more frame *) IF ( (DS:RBX).CSSA ≥ (DS:RBX).NSSA) THEN #GP(0); FI;

(* Compute linear address of SSA frame *)

TMP_SSA := (DS:RBX).OSSA + TMP_SECS.BASEADDR + 4096 * TMP_SECS.SSAFRAMESIZE * (DS:RBX).CSSA;

TMP_XSIZE := compute_XSAVE_frame_size(TMP_SECS.ATTRIBUTES.XFRM);

FOR EACH TMP_SSA_PAGE = TMP_SSA to TMP_SSA + TMP_XSIZE

(* Check page is read/write accessible *)

Check that DS:TMP_SSA_PAGE is read/write accessible;

If a fault occurs, release locks, abort, and deliver that fault;

IF (DS:TMP_SSA_PAGE does not resolve to EPC page)

THEN #PF(DS:TMP_SSA_PAGE); FI;

IF (EPCM(DS:TMP_SSA_PAGE).VALID = 0)

THEN #PF(DS:TMP_SSA_PAGE); FI;

IF (EPCM(DS:TMP_SSA_PAGE).BLOCKED = 1)

THEN #PF(DS:TMP_SSA_PAGE); FI;

IF ((EPCM(DS:TMP_SSA_PAGE).PENDING = 1) or (EPCM(DS:TMP_SSA_PAGE).MODIFIED = 1))

THEN #PF(DS:TMP_SSA_PAGE); FI;

IF ( ( EPCM(DS:TMP_SSA_PAGE).ENCLAVEADDRESS ≠ DS:TMP_SSA_PAGE) or (EPCM(DS:TMP_SSA_PAGE).PT ≠ PT_REG) or

(EPCM(DS:TMP_SSA_PAGE).ENCLAVESECS ≠ EPCM(DS:RBX).ENCLAVESECS) or

(EPCM(DS:TMP_SSA_PAGE).R = 0) or (EPCM(DS:TMP_SSA_PAGE).W = 0) )

THEN #PF(DS:TMP_SSA_PAGE); FI;

CR_XSAVE_PAGE_n := Physical_Address(DS:TMP_SSA_PAGE);

ENDFOR

(* Compute address of GPR area*)

TMP_GPR := TMP_SSA + 4096 * DS:TMP_SECS.SSAFRAMESIZE - sizeof(GPRSGX_AREA);

If a fault occurs; release locks, abort, and deliver that fault;

IF (DS:TMP_GPR does not resolve to EPC page)

THEN #PF(DS:TMP_GPR); FI;

IF (EPCM(DS:TMP_GPR).VALID = 0)

THEN #PF(DS:TMP_GPR); FI;

IF (EPCM(DS:TMP_GPR).BLOCKED = 1)

THEN #PF(DS:TMP_GPR); FI;

IF ((EPCM(DS:TMP_GPR).PENDING = 1) or (EPCM(DS:TMP_GPR).MODIFIED = 1))

THEN #PF(DS:TMP_GPR); FI;

IF ( ( EPCM(DS:TMP_GPR).ENCLAVEADDRESS ≠ DS:TMP_GPR) or (EPCM(DS:TMP_GPR).PT ≠ PT_REG) or

(EPCM(DS:TMP_GPR).ENCLAVESECS EPCM(DS:RBX).ENCLAVESECS) or

(EPCM(DS:TMP_GPR).R = 0) or (EPCM(DS:TMP_GPR).W = 0) )

THEN #PF(DS:TMP_GPR); FI;

IF (TMP_MODE64 = 0)

THEN

IF (TMP_GPR + (GPR_SIZE -1) is not in DS segment) THEN #GP(0); FI;

FI;

CR_GPR_PA := Physical_Address (DS: TMP_GPR);

(* Validate TCS.OENTRY *)

TMP_TARGET := (DS:RBX).OENTRY + TMP_SECS.BASEADDR;

IF (TMP_MODE64 = 1)

THEN

IF (TMP_TARGET is not canonical) THEN #GP(0); FI;

ELSE

IF (TMP_TARGET > CS limit) THEN #GP(0); FI;

FI;

(* Check proposed FS/GS segments fall within DS *)

IF (TMP_MODE64 = 0)

THEN

TMP_FSBASE := (DS:RBX).OFSBASE + TMP_SECS.BASEADDR;

TMP_FSLIMIT := (DS:RBX).OFSBASE + TMP_SECS.BASEADDR + (DS:RBX).FSLIMIT;

TMP_GSBASE := (DS:RBX).OGSBASE + TMP_SECS.BASEADDR;

TMP_GSLIMIT := (DS:RBX).OGSBASE + TMP_SECS.BASEADDR + (DS:RBX).GSLIMIT;

(* if FS wrap-around, make sure DS has no holes*)

IF (TMP_FSLIMIT < TMP_FSBASE)

THEN

IF (DS.limit < 4GB) THEN #GP(0); FI;

ELSE

IF (TMP_FSLIMIT > DS.limit) THEN #GP(0); FI;

FI;

(* if GS wrap-around, make sure DS has no holes*)

IF (TMP_GSLIMIT < TMP_GSBASE)

THEN

IF (DS.limit < 4GB) THEN #GP(0); FI;

ELSE

IF (TMP_GSLIMIT > DS.limit) THEN #GP(0); FI;

FI;

ELSE

TMP_FSBASE := (DS:RBX).OFSBASE + TMP_SECS.BASEADDR;

TMP_GSBASE := (DS:RBX).OGSBASE + TMP_SECS.BASEADDR;

IF ( (TMP_FSBASE is not canonical) or (TMP_GSBASE is not canonical))

THEN #GP(0); FI;

FI;

(* Ensure the enclave is not already active and this thread is the only one using the TCS*)

IF (DS:RBX.STATE = ACTIVE)

THEN #GP(0); FI;

TMP_IA32_U_CET := 0

TMP_SSP : = 0

IF CPUID.(EAX=12H, ECX=1):EAX[6] = 1

THEN

IF ( CR4.CET = 0 )

THEN

(* If part does not support CET or CET has not been enabled and enclave requires CET then fail *)

IF ( TMP_SECS.CET_ATTRIBUTES ≠ 0 OR TMP_SECS.CET_LEG_BITMAP_OFFSET ≠ 0 ) #GP(0); FI;

FI;

(* If indirect branch tracking or shadow stacks enabled but CET state save area is not 16B aligned then fail EENTER *)

IF ( TMP_SECS.CET_ATTRIBUTES.SH_STK_EN = 1 OR TMP_SECS.CET_ATTRIBUTES.ENDBR_EN = 1 )

THEN

IF (DS:RBX.OCETSSA is not 16B aligned) #GP(0); FI;

FI;

IF (TMP_SECS.CET_ATTRIBUTES.SH_STK_EN OR TMP_SECS.CET_ATTRIBUTES.ENDBR_EN)

THEN

(* Setup CET state from SECS, note tracker goes to IDLE *)

TMP_IA32_U_CET = TMP_SECS.CET_ATTRIBUTES;

IF (TMP_IA32_U_CET.LEG_IW_EN = 1 AND TMP_IA32_U_CET.ENDBR_EN = 1 )

THEN

TMP_IA32_U_CET := TMP_IA32_U_CET + TMP_SECS.BASEADDR;

TMP_IA32_U_CET := TMP_IA32_U_CET + TMP_SECS.CET_LEG_BITMAP_BASE;

FI;

(* Compute linear address of what will become new CET state save area and cache its PA *)

TMP_CET_SAVE_AREA = DS:RBX.OCETSSA + TMP_SECS.BASEADDR + (DS:RBX.CSSA) * 16

TMP_CET_SAVE_PAGE = TMP_CET_SAVE_AREA & ~0xFFF;

Check the TMP_CET_SAVE_PAGE page is read/write accessible

If fault occurs release locks, abort, and deliver fault

(* Read the EPCM VALID, PENDING, MODIFIED, BLOCKED, and PT fields atomically *)

IF ((DS:TMP_CET_SAVE_PAGE Does NOT RESOLVE TO EPC PAGE) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).VALID = 0) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).PENDING = 1) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).MODIFIED = 1) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).BLOCKED = 1) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).R = 0) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).W = 0) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).ENCLAVEADDRESS ≠ DS:TMP_CET_SAVE_PAGE) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).PT ≠ PT_SS_REST) OR

(EPCM(DS:TMP_CET_SAVE_PAGE).ENCLAVESECS ≠ EPCM(DS:RBX).ENCLAVESECS))

THEN

#PF(DS:TMP_CET_SAVE_PAGE);

FI;

CR_CET_SAVE_AREA_PA := Physical address(DS:TMP_CET_SAVE_AREA)

IF TMP_IA32_U_CET.SH_STK_EN = 1

THEN

TMP_SSP = TCS.PREVSSP;

FI;

FI;

CR_ENCLAVE_MODE := 1;

CR_ACTIVE_SECS := TMP_SECS;

CR_ELRANGE := (TMPSECS.BASEADDR, TMP_SECS.SIZE);

(* Save state for possible AEXs *)

CR_TCS_PA := Physical_Address (DS:RBX);

CR_TCS_LA := RBX;

CR_TCS_LA.AEP := RCX;

(* Save the hidden portions of FS and GS *)

CR_SAVE_FS_selector := FS.selector;

CR_SAVE_FS_base := FS.base;

CR_SAVE_FS_limit := FS.limit;

CR_SAVE_FS_access_rights := FS.access_rights;

CR_SAVE_GS_selector := GS.selector;

CR_SAVE_GS_base := GS.base;

CR_SAVE_GS_limit := GS.limit;

CR_SAVE_GS_access_rights := GS.access_rights;

(* If XSAVE is enabled, save XCR0 and replace it with SECS.ATTRIBUTES.XFRM*)

IF (CR4.OSXSAVE = 1)

CR_SAVE_XCR0 := XCR0;

XCR0 := TMP_SECS.ATTRIBUTES.XFRM;

FI;

RCX := RIP;

RIP := TMP_TARGET;

RAX := (DS:RBX).CSSA;

(* Save the outside RSP and RBP so they can be restored on interrupt or EEXIT *)

DS:TMP_SSA.U_RSP := RSP;

DS:TMP_SSA.U_RBP := RBP;

(* Do the FS/GS swap *)

FS.base := TMP_FSBASE;

FS.limit := DS:RBX.FSLIMIT;

FS.type := 0001b;

FS.W := DS.W;

FS.S := 1;

FS.DPL := DS.DPL;

FS.G := 1;

FS.B := 1;

FS.P := 1;

FS.AVL := DS.AVL;

FS.L := DS.L;

FS.unusable := 0;

FS.selector := 0BH;

GS.base := TMP_GSBASE;

GS.limit := DS:RBX.GSLIMIT;

GS.type := 0001b;

GS.W := DS.W;

GS.S := 1;

GS.DPL := DS.DPL;

GS.G := 1;

GS.B := 1;

GS.P := 1;

GS.AVL := DS.AVL;

GS.L := DS.L;

GS.unusable := 0;

GS.selector := 0BH;

CR_DBGOPTIN := TCS.FLAGS.DBGOPTIN;

Suppress_all_code_breakpoints_that_are_outside_ELRANGE;

IF (CR_DBGOPTIN = 0)

THEN

Suppress_all_code_breakpoints_that_overlap_with_ELRANGE;

CR_SAVE_TF := RFLAGS.TF;

RFLAGS.TF := 0;

Suppress_monitor_trap_flag for the source of the execution of the enclave;

Suppress any pending debug exceptions;

Suppress any pending MTF VM exit;

ELSE

IF RFLAGS.TF = 1

THEN pend a single-step #DB at the end of EENTER; FI;

IF the “monitor trap flag” VM-execution control is set

THEN pend an MTF VM exit at the end of EENTER; FI;

FI;

IF ((CPUID.(EAX=7H, ECX=0):EDX[CET_IBT] = 1) OR (CPUID.(EAX=7H, ECX=0):ECX[CET_SS] = 1)

THEN

(* Save enclosing application CET state into save registers *)

CR_SAVE_IA32_U_CET := IA32_U_CET

(* Setup enclave CET state *)

IF CPUID.(EAX=07H, ECX=00h):ECX[CET_SS] = 1

THEN

CR_SAVE_SSP := SSP

SSP := TMP_SSP

FI;

IA32_U_CET := TMP_IA32_U_CET;

FI;

Flush_linear_context;

Allow_front_end_to_begin_fetch_at_new_RIP;

Flags Affected

RFLAGS.TF is cleared on opt-out entry.

Protected Mode Exceptions

#GP(0) If DS:RBX is not page aligned.
If the enclave is not initialized.
If part or all of the FS or GS segment specified by TCS is outside the DS segment or not properly aligned.
If the thread is not in the INACTIVE state.
If CS, DS, ES or SS bases are not all zero.
If executed in enclave mode.
If any reserved field in the TCS FLAG is set.
If the target address is not within the CS segment.
If CR4.OSFXSR = 0.
If CR4.OSXSAVE = 0 and SECS.ATTRIBUTES.XFRM ≠ 3.
If CR4.OSXSAVE = 1and SECS.ATTRIBUTES.XFRM is not a subset of XCR0.
If SECS.ATTRIBUTES.AEXNOTIFY ≠ TCS.FLAGS.AEXNOTIFY and TCS.FLAGS.DBGOPTIN = 0.
#PF(error code) If a page fault occurs in accessing memory.
If DS:RBX does not point to a valid TCS.
If one or more pages of the current SSA frame are not readable/writable, or do not resolve to a valid PT_REG EPC page.

64-Bit Mode Exceptions

#GP(0) If DS:RBX is not page aligned.
If the enclave is not initialized.
If the thread is not in the INACTIVE state.
If CS, DS, ES or SS bases are not all zero.
If executed in enclave mode.
If part or all of the FS or GS segment specified by TCS is outside the DS segment or not properly aligned.
If the target address is not canonical.
If CR4.OSFXSR = 0.
If CR4.OSXSAVE = 0 and SECS.ATTRIBUTES.XFRM ≠ 3.
If CR4.OSXSAVE = 1and SECS.ATTRIBUTES.XFRM is not a subset of XCR0.
If SECS.ATTRIBUTES.AEXNOTIFY ≠ TCS.FLAGS.AEXNOTIFY and TCS.FLAGS.DBGOPTIN = 0.
#PF(error code) If a page fault occurs in accessing memory operands.
If DS:RBX does not point to a valid TCS.
If one or more pages of the current SSA frame are not readable/writable, or do not resolve to a valid PT_REG EPC page.