/* * Copyright (C) 2011-2017 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * Description: * The file provides `enclave_entry' function to switch code between * trusted and untrusted envronment. */ .file "trts_pic.S" #include "trts_pic.h" .text DECLARE_LOCAL_FUNC get_enclave_base lea_pic __ImageBase, %xax ret DECLARE_LOCAL_FUNC get_enclave_state lea_pic g_enclave_state, %xcx xor %xax, %xax movl (%xcx), %eax ret DECLARE_LOCAL_FUNC set_enclave_state lea_pic g_enclave_state, %xax #ifdef LINUX32 mov SE_WORDSIZE(%esp), %edi #endif movl %edi, (%xax) ret DECLARE_LOCAL_FUNC lock_enclave lea_pic g_enclave_state, %xdx xor %xax, %xax mov $ENCLAVE_INIT_NOT_STARTED, %eax xor %xcx, %xcx mov $ENCLAVE_INIT_IN_PROGRESS, %ecx /* if (g_global_data.enclave_state == ENCLAVE_INIT_NOT_STARTED) */ lock cmpxchgl %ecx, (%xdx) /* g_global_data.enclave_state == ENCLAVE_INIT_IN_PROGRESS */ ret /* xax: the initial value of enclave state */ /* * --------------------------------------------------------------------- * Function: thread_data_t* get_thread_data(void); * * Get the address of thread_data * --------------------------------------------------------------------- */ DECLARE_LOCAL_FUNC get_thread_data READ_TD_DATA self_addr ret /* * --------------------------------------------------------------------- * Function: enclave_entry * The entry point of the enclave. * * Registers: * XAX - TCS.CSSA * XBX - the address of a TCS * XCX - the address of the instruction following the EENTER * XDI - the reason of entering the enclave * XSI - the pointer to the marshalling structure */ DECLARE_GLOBAL_FUNC enclave_entry /* * ---------------------------------------------------------------------- * Dispatch code according to CSSA and the reason of EENTER * eax > 0 - exception handler * edi >= 0 - ecall * edi == -1 - do_init_enclave * edi == -2 - oret * Registers * No need to use any register during the dipatch * ---------------------------------------------------------------------- */ .cfi_startproc cmp $0, %xax jne .Ldo_handler /* handle exception state */ xor %xdx, %xdx READ_TD_DATA last_sp cmp $0, %xax jne .Lswitch_stack GET_STACK_BASE %xbx /* if last_sp == 0, set sp to stack base */ sub $STATIC_STACK_SIZE, %xax /* give space for static stack */ .Lswitch_stack: xchg %xsp, %xax push %xcx push %xbp .cfi_def_cfa_offset 2 * SE_WORDSIZE .cfi_offset xbp, -2 * SE_WORDSIZE mov %xsp, %xbp .cfi_def_cfa_register xbp /* Save the registers */ sub $(6*SE_WORDSIZE), %xsp mov %xax, -1*SE_WORDSIZE(%xbp) /* xsp_u */ mov %xdx, -3*SE_WORDSIZE(%xbp) /* cssa */ mov %xbx, -4*SE_WORDSIZE(%xbp) /* TCS */ mov %xsi, -5*SE_WORDSIZE(%xbp) /* XSI */ mov %xdi, -6*SE_WORDSIZE(%xbp) /* XDI */ #ifdef LINUX64 mov %rdx, %rcx mov %rbx, %rdx #endif call enter_enclave .Lexit_enclave: mov -1*SE_WORDSIZE(%xbp), %xdx /* xdx: xsp_u */ mov %xbp, %xsp pop %xbp /* xbp_u */ pop %xbx /* ret_u */ mov %xdx, %xsp /* xsp_u */ mov $OCMD_ERET, %xdi mov %xax, %xsi .Lclear_and_exit_enclave: /* Clear all GPRs, except xax, xbx, xdi and xsi */ xor %xcx, %xcx xor %xdx, %xdx #if defined(LINUX64) xor %r8, %r8 xor %r9, %r9 xor %r10, %r10 xor %r11, %r11 xor %r12, %r12 xor %r13, %r13 xor %r14, %r14 xor %r15, %r15 #endif /* Set status flags to pre-defined values */ add %xdx, %xdx /* OF = SF = AF = CF = 0; ZF = PF = 1 */ /* EEXIT */ mov $SE_EEXIT, %xax /* EEXIT leaf */ ENCLU /* Should not come here */ ud2 .Ldo_handler: mov %xax, %xdx /* XDX: cssa */ GET_STACK_BASE %xbx /* XAX: static stack, set sp to stack base */ jmp .Lswitch_stack /* Should not come here */ ud2 .cfi_endproc /* * ------------------------------------------------------------------------- * sgx_status_t do_ocall(unsigned int index, void *ms); * * Function: do_ocall * The entry point of the enclave * Parameters: * func_addr - target function address * ms - marshalling structure * * Stack: (same as do_oret) * bottom of stack -> * ----------------- * | ECALL/OCALL | * previous TD.last_sp -> | frames | * ----------------- * | ECALL frame | * | do_ocall param 2| 21 * | do_ocall param 1| 20 * |do_ocall ret_addr| 19 * | ocall_depth | 18 * | reserved | 17 * | reserved | 16 * | reserved | 15 * | rbx | 14 * | rsi | 13 * | rdi | 12 * | rbp | 11 * | r12 | 10 * | r13 | 9 * | r14 | 8 * | r15 | 7 * | prev TD.last_sp | 6 * | ocall_index | 5 * | OCALL FLAG | 4 * | shadow | 3 * | shadow | 2 * | shadow | 1 * TD.last_sp -> | shadow | 0 * ----------------- * ------------------------------------------------------------------------- */ DECLARE_LOCAL_FUNC do_ocall /* * 8 for GPR, 1 for TD.last_sp, 1 for ocall_index * 1 for OCALL_FLAG, 4 for shadow space. * Stack Pointer is 16-byte aligned under x86_64. */ sub $(19*SE_WORDSIZE), %xsp /* save non-volatile registers, except xsp */ mov %xbx, SE_WORDSIZE*14(%xsp) mov %xsi, SE_WORDSIZE*13(%xsp) mov %xdi, SE_WORDSIZE*12(%xsp) mov %xbp, SE_WORDSIZE*11(%xsp) #ifdef LINUX64 mov %r12, SE_WORDSIZE*10(%rsp) mov %r13, SE_WORDSIZE* 9(%rsp) mov %r14, SE_WORDSIZE* 8(%rsp) mov %r15, SE_WORDSIZE* 7(%rsp) #endif /* set xdi and xsi using the input parameters */ #ifdef LINUX64 mov %edi, %edi /* it should clear the high 32bit word of RDI */ /* * rdi - param 1 (index), rsi - param 2 (ms) * only use lower 32bit of rdi, rsi remains unchanged. */ #endif #ifdef LINUX32 mov SE_WORDSIZE*20(%esp), %edi mov SE_WORDSIZE*21(%esp), %esi #endif /* save ocall index to the stack */ mov $OCALL_FLAG, %xax mov %xax, SE_WORDSIZE*4(%xsp) /* save OCALL_FLAG */ mov %xdi, SE_WORDSIZE*5(%xsp) /* save ocall_index */ /* * save the inside stack context * push TD.last_sp * set TD.last_sp = xsp */ READ_TD_DATA self_addr mov %xax, %xbx /* call update_ocall_lastsp */ #ifdef LINUX32 mov %xsp, (%xsp) #else mov %xsp, %xdi #endif call update_ocall_lastsp /* xax: td.last_sp */ #ifdef LINUX64 mov SE_WORDSIZE*12(%xsp), %xdi /* restore xdi */ mov SE_WORDSIZE*13(%xsp), %xsi /* restore xdi */ #endif /* restore outside stack context */ mov first_ssa_gpr(%xbx), %xdx mov ssa_bp_u(%xdx), %xbp mov ssa_sp_u(%xdx), %xsp /* * set EEXIT registers * return address can be read from the ECALL frame: * TD.last_sp -> * ------------- * | ret_addr | * | xbp_u | * | xsp_u | * | ... | */ mov -1*SE_WORDSIZE(%xax), %xbx /* return address */ mov $SE_EEXIT, %xax /* EEXIT leaf */ /* Clear all GPRs, except xax, xbx, xdi, and xsi*/ xor %xcx, %xcx xor %xdx, %xdx #ifdef LINUX64 xor %r8, %r8 xor %r9, %r9 xor %r10, %r10 xor %r11, %r11 xor %r12, %r12 xor %r13, %r13 xor %r14, %r14 xor %r15, %r15 #endif /* Set status flags to pre-defined values */ add %xdx, %xdx /* OF = SF = AF = CF = 0; ZF = PF = 1 */ ENCLU /* * ------------------------------------------------------------------ * this function is the wrapper of do_ocall, which is used to * stick ocall bridge and proxy frame together * ------------------------------------------------------------------ */ DECLARE_LOCAL_FUNC __morestack .cfi_startproc push %xbp .cfi_def_cfa_offset 2*SE_WORDSIZE .cfi_offset xbp,-2*SE_WORDSIZE mov %xsp, %xbp .cfi_def_cfa_register xbp sub $(4*SE_WORDSIZE), %xsp #ifdef LINUX32 /* save the 2 parameters */ mov (2*SE_WORDSIZE)(%xbp), %xax mov %xax, (0*SE_WORDSIZE)(%xsp) mov (3*SE_WORDSIZE)(%xbp), %xax mov %xax, (1*SE_WORDSIZE)(%xsp) #endif call do_ocall leave ret .cfi_endproc DECLARE_GLOBAL_FUNC asm_oret #ifdef LINUX32 mov SE_WORDSIZE(%xsp), %xdi mov 2*SE_WORDSIZE(%xsp), %xsi #endif mov %xdi, %xsp /* restore thread_data.last_sp */ mov %xsi, %xax /* ocall return value */ #ifdef LINUX64 mov 7*SE_WORDSIZE(%xsp), %r15 mov 8*SE_WORDSIZE(%xsp), %r14 mov 9*SE_WORDSIZE(%xsp), %r13 mov 10*SE_WORDSIZE(%xsp), %r12 #endif mov 11*SE_WORDSIZE(%xsp), %xbp mov 12*SE_WORDSIZE(%xsp), %xdi mov 13*SE_WORDSIZE(%xsp), %xsi mov 14*SE_WORDSIZE(%xsp), %xbx add $(19*SE_WORDSIZE), %xsp ret /* should not come here */ ud2 /* * ------------------------------------------------------------------------ * extern "C" int do_egetkey(key_request_t *key_request, key_128bit_t *key) * return value: * 0 - success * none-zeor - EGETKEY error code * EGETKEY: rbx - the address of KEYREQUEST structure * rcx - the address where the key is outputted * ------------------------------------------------------------------------ */ DECLARE_LOCAL_FUNC do_egetkey SE_PROLOG mov $SE_EGETKEY, %xax /* EGETKEY leaf */ ENCLU #ifdef SE_SIM cmp $SGX_SUCCESS, %xax /* In simulation mode, ZF flag will not be set */ jnz .Legetkey_done /* because the stack clean operation will always clean ZF flag */ #else jz .Legetkey_done /* if EGETKEY error, ZF flag is set and error code is set to xax */ #endif xor %xax, %xax .Legetkey_done: SE_EPILOG ret /* * ------------------------------------------------------------------------- * extern "C" void do_ereport(sgx_target_info_t *target_info, sgx_report_data_t *report_data, sgx_report_t *report); * EREPORT: rbx - the address of TARGETINFO; * rcx - the address of REPORTDATA; * rdx - the address where REPORT is outputted * ------------------------------------------------------------------------- */ DECLARE_LOCAL_FUNC do_ereport SE_PROLOG mov $SE_EREPORT, %xax /* EREPORT leaf */ ENCLU SE_EPILOG ret #define _RDRAND_RETRY_TIMES 10 /* * ------------------------------------- * extern "C" uint32_t do_rdrand(uint32_t *rand); * return value: * non-zero: rdrand succeeded * zero: rdrand failed * ------------------------------------- */ DECLARE_LOCAL_FUNC do_rdrand mov $_RDRAND_RETRY_TIMES, %ecx .Lrdrand_retry: .byte 0x0F, 0xC7, 0xF0 /* rdrand %eax */ jc .Lrdrand_return dec %ecx jnz .Lrdrand_retry xor %xax, %xax ret .Lrdrand_return: #ifdef LINUX32 mov SE_WORDSIZE(%esp), %ecx #else mov %rdi, %rcx #endif movl %eax, (%xcx) mov $1, %xax ret /* * ------------------------------------------------------------------------- * extern "C" void abort(void) __attribute__(__noreturn__); * ------------------------------------------------------------------------- */ DECLARE_LOCAL_FUNC abort lea_pic g_enclave_state, %xax movl $ENCLAVE_CRASHED, (%xax) ud2 /* * ------------------------------------------------------------------------- * extern "C" __attribute__((regparm(1))) void continue_execution(sgx_exception_info_t *info); * ------------------------------------------------------------------------- */ DECLARE_LOCAL_FUNC continue_execution #ifdef LINUX32 mov %xax, %xcx #else mov %xdi, %xcx #endif mov SE_WORDSIZE*0(%xcx), %xax push %xax # push xax mov SE_WORDSIZE*1(%xcx), %xax push %xax # push xcx mov SE_WORDSIZE*4(%xcx), %xax sub $(SE_WORDSIZE), %xax # xax: xsp # restore registers except xax, xcx, xsp mov SE_WORDSIZE*2(%xcx), %xdx mov SE_WORDSIZE*3(%xcx), %xbx mov SE_WORDSIZE*5(%xcx), %xbp mov SE_WORDSIZE*6(%xcx), %xsi mov SE_WORDSIZE*7(%xcx), %xdi #ifdef LINUX64 mov SE_WORDSIZE*8(%xcx), %r8 mov SE_WORDSIZE*9(%xcx), %r9 mov SE_WORDSIZE*10(%xcx), %r10 mov SE_WORDSIZE*11(%xcx), %r11 mov SE_WORDSIZE*12(%xcx), %r12 mov SE_WORDSIZE*13(%xcx), %r13 mov SE_WORDSIZE*14(%xcx), %r14 mov SE_WORDSIZE*15(%xcx), %r15 push SE_WORDSIZE*16(%xcx) popf # make sure the following instructions do not affect flags #else push SE_WORDSIZE*8(%xcx) popf #endif #ifdef LINUX64 mov SE_WORDSIZE*17(%xcx), %xcx #else mov SE_WORDSIZE*9(%xcx), %xcx # xcx: xip #endif # do not setup the new stack until info is not needed any more # otherwise, info will be overwritten mov %xcx, (%xax) # save xip to the new stack pop %xcx # restore xcx pop %xsp # xsp: xax xchg %xax, %xsp ret