/* * The file implements vIDT ISR flows. * Copyright (c) 2014, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #ifndef APP_MODE #include #include #include #include #include #include #include "include/vmm_hsym.h" #include "include/vmm_hsym_common.h" #include "include/vidt_ioctl.h" #include "include/types.h" #include "include/arch72.h" #include #include #endif //#include #define ACTIVATE_ACR3 #define APP_MODE 1 __asm__ ( \ ".intel_syntax noprefix\n" \ "patch_str_secs_ptr = 0xAABBCCDDAABBCCDD\n" \ "patch_str_core_id = 0xBBCCDDAABBCCDDAA\n" \ "patch_str_core_state_ptr = 0xCCDDAABBCCDDAABB\n" \ "patch_str_secs_scv = 0xDDCCAABBDDCCAABB\n" \ "patch_str_region_addr = 0xAACCBBDDAACCBBDD\n" \ "patch_str_idt_vector = 0xAADDBBCCAADDBBCC\n" \ "patch_str_enter_page = 0xBBDDAACCBBDDAACC\n" \ "patch_str_exit_page = 0xCCAADDBBCCAADDBB\n" \ "patch_str_secs_scv_un = 0xDDBBAACCDDBBAACC\n" \ ".att_syntax\n" \ ); // *********************************************** //!!!!!!!!!!!!!!!!!! NOTE WELL !!!!!!!!!!!!!!!!!!! //if this code is changed, the entrypoint //offset added to the MyHandler base in read_idt //must be updated to ensure the correct entrypoints //are registered in the vIDT //Also NOTE - mov for val is used to ensure opcode //does not change with operand size, so that size //of this code block does not change // ************************************************ void vidt_stub_patch_val0(void); void begin_vidt_stub0(void); void vidt_stub_patch_callee0(void); #define VIDT_STUB(errcode,val) \ __asm__ ( \ ".intel_syntax noprefix\n" \ ".globl begin_vidt_stub0\n" \ ".globl vidt_stub_patch_val0\n" \ ".globl vidt_stub_patch_callee0\n" \ \ "begin_vidt_stub"val":\n" \ "push r8;\n" \ "push r9;\n" \ "push r10;\n" \ "push r11;\n" \ "push r12;\n" \ "push r13;\n" \ "push r14;\n" \ "push r15;\n" \ "push rdi;\n" \ "push rsi;\n" \ "push rbx;\n" \ "push rdx;\n" \ "push rcx;\n" \ "push rax;\n" \ \ "xor rdi, rdi;\n" \ "vidt_stub_patch_val"val":\n" \ "mov rdi, patch_str_idt_vector;\n" \ "xor rsi, rsi;\n" \ "mov esi, "errcode";\n" \ "vidt_stub_patch_callee"val":\n" \ "mov rax, patch_str_region_addr;\n" \ "call rax;\n" \ \ "cmp eax, 0;\n" \ "je vidt_stub_use_iret"val";\n" \ "pop rax;\n" \ "pop rcx;\n" \ "pop rdx;\n" \ "pop rbx;\n" \ "pop rsi;\n" \ "pop rdi;\n" \ "pop r15;\n" \ "pop r14;\n" \ "pop r13;\n" \ "pop r12;\n" \ "pop r11;\n" \ "pop r10;\n" \ "pop r9;\n" \ "xchg r8, [rsp];\n" \ "ret;\n" \ \ "vidt_stub_use_iret"val":\n" \ "pop rax;\n" \ "pop rcx;\n" \ "pop rdx;\n" \ "pop rbx;\n" \ "pop rsi;\n" \ "pop rdi;\n" \ "pop r15;\n" \ "pop r14;\n" \ "pop r13;\n" \ "pop r12;\n" \ "pop r11;\n" \ "pop r10;\n" \ "pop r9;\n" \ "pop r8;\n" \ "iretq;\n" \ ".att_syntax\n" \ ); void MyHandler(void); __asm__ ( ".intel_syntax noprefix\n" ".globl MyHandler\n" "MyHandler:\n" //#DE "vidt_entry_0:\n" ); VIDT_STUB("0","0"); __asm__ ( //#DB "vidt_entry_1:\n" ); VIDT_STUB("0","1"); __asm__ ( //#NMI "vidt_entry_2:\n" ); VIDT_STUB("0","2"); __asm__ ( //#BP "vidt_entry_3:\n" ); VIDT_STUB("0","3"); __asm__ ( //#OF "vidt_entry_4:\n" ); VIDT_STUB("0","4"); __asm__ ( //#BR "vidt_entry_5:\n" ); VIDT_STUB("0","5"); __asm__ ( //#UD "vidt_entry_6:\n" ); VIDT_STUB("0","6"); __asm__ ( //#NM "vidt_entry_7:\n" ); VIDT_STUB("0","7"); __asm__ ( //#DF "vidt_entry_8:\n" ); VIDT_STUB("1","8"); //errcode=1 __asm__ ( //CSO Abort "vidt_entry_9:\n" ); VIDT_STUB("0","9"); __asm__ ( //#TS "vidt_entry_10:\n" ); VIDT_STUB("1","10"); //errcode=1 __asm__ ( //#NP "vidt_entry_11:\n" ); VIDT_STUB("1","11"); //errcode=1 __asm__ ( //#SS "vidt_entry_12:\n" ); VIDT_STUB("1","12"); //errcode=1 __asm__ ( //#GP "vidt_entry_13:\n" ); VIDT_STUB("1","13"); //errcode=1 __asm__ ( //#PF "vidt_entry_14:\n" ); VIDT_STUB("1","14"); //errcode=1 __asm__ ( //RESV "vidt_entry_15:\n" ); VIDT_STUB("0","15"); __asm__ ( //#MF "vidt_entry_16:\n" ); VIDT_STUB("0","16"); __asm__ ( //#AC "vidt_entry_17:\n" ); VIDT_STUB("1","17"); //errcode=1 __asm__ ( //#MC "vidt_entry_18:\n" ); VIDT_STUB("0","18"); __asm__ ( //#XM "vidt_entry_19:\n" ); VIDT_STUB("0","19"); __asm__ ( //#VE "vidt_entry_20:\n" ); VIDT_STUB("0","20"); ///////////////////////////////// arch handlers end //TBD NEED TO FILL IN OTHER ISRS uptil index 255 //THOSE ARE COPIED into memory in the code //that sets up the per-core vIDT code pages __asm__ ( ".att_syntax\n" ); void MyHandler_arch_end(void); __asm__ ( ".globl MyHandler_arch_end\n" "MyHandler_arch_end:" ); VIDT_STUB("0","99"); //dummy ISR for size calculation void MyHandler_non_arch_end(void); __asm__ ( ".globl MyHandler_non_arch_end\n" "MyHandler_non_arch_end:" "nop;\n" ); //AEX - ASYNCHRONOUS EXIT VIEW FLOW //NOTE - expected to be called only from MyHandler code //Expects to get a flag param on stack to skip error code //based on IDT vector - some entries below 20 have error code void test_code_cpuindex(void); void test_code_ptr_core_state_patch(void); void test_code_secs_patch1(void); void test_code_secs_patch2(void); void test_code_cmp_patch(void); void test_code_exit_page_patch(void); void test_code(void); __asm__ ( ".intel_syntax noprefix\n" ".globl test_code_cpuindex\n" ".globl test_code_ptr_core_state_patch\n" ".globl test_code_secs_patch1\n" ".globl test_code_secs_patch2\n" ".globl test_code_cmp_patch\n" ".globl test_code_exit_page_patch\n" ".globl test_code\n" "params_size =0\n" //stack params offsets "flag =4\n" "vector =8\n" "gpr_frame_size =112\n" //offset to gprs on stack "start_gprs =params_size + 8\n" //offsets for gprs with base at start_gprs "gprs.eax =0\n" "gprs.ecx =8\n" "gprs.edx =16\n" "gprs.ebx =24\n" "gprs.esi =32\n" "gprs.edi =40\n" "gprs.r15 =48\n" "gprs.r14 =56\n" "gprs.r13 =64\n" "gprs.r12 =72\n" "gprs.r11 =80\n" "gprs.r10 =88\n" "gprs.r9 =96\n" "gprs.r8 =104\n" //intr_frame offsets "if_skip_ec =start_gprs + gpr_frame_size + 8\n" //skips errcode "if_noskip_ec =start_gprs + gpr_frame_size\n" //does not skip errcode "if.errcode =0\n" //used with if_noskip_ec offset //these offsets are with reference base if_skip_ec and if_noskip_ec: "if.eip =0\n" "if.cs =8\n" "if.eflags =16\n" "if.esp =24\n" "if.ss =32\n" //secs offsets from arch72.h "secs.size =0\n" "secs.base =8\n" "secs.ssa_frame_size =16\n" "secs.attrib =48\n" "secs.eid =260\n" "secs.ept_enabled =3559\n" "secs.acr3 =3560\n" "secs.scv =3568\n" "secs.os_cr3 =3576\n" "secs.pcd =3584\n" //per core data //secs offsets for per core data "secs.pcd.tcs =0\n" "secs.pcd.idtr =8\n" "secs.pcd.gdtr =16\n" "secs.pcd.ldtr =24\n" "secs.pcd.tr =32\n" "secs.pcd.r0sp =40\n" "shift_size_secs_pcd =6\n" //pcd size is 64 currently "shift_size_4k =12\n" //ssa frame size is 4k //tcs offsets from arch72.h "tcs.state =0\n" "tcs.flags =8\n" "tcs.ossa =16\n" "tcs.cssa =24\n" "tcs.nssa =28\n" "tcs.oentry =32\n" "tcs.aep =40\n" "tcs.ofs_base =48\n" "tcs.ogs_base =56\n" "tcs.ofs_limit =64\n" "tcs.ogs_limit =68\n" "tcs.save_fs_selector =72\n" "tcs.save_fs_desc_low =74\n" "tcs.save_fs_desc_high =78\n" "tcs.save_gs_selector =82\n" "tcs.save_gs_desc_low =84\n" "tcs.save_gs_desc_high =88\n" "tcs.ssa =92\n" "tcs.eflags =96\n" "tcs.os_cr3 =100\n" "tcs.ur0sp =108\n" "tcs_state_expect_active =1\n" "tcs_state_set_inactive =0\n" //note ssa gpr area at end of page "ssa_gpr_size =168\n" //ssa offsets from arch72.h "ssa.ax =0\n" "ssa.cx =8\n" "ssa.dx =16\n" "ssa.bx =24\n" "ssa.sp =32\n" "ssa.bp =40\n" "ssa.si =48\n" "ssa.di =56\n" "ssa.r8 =64\n" "ssa.r9 =72\n" "ssa.r10 =80\n" "ssa.r11 =88\n" "ssa.r12 =96\n" "ssa.r13 =104\n" "ssa.r14 =112\n" "ssa.r15 =120\n" "ssa.flags =128\n" "ssa.ip =136\n" "ssa.sp_u =144\n" "ssa.bp_u =152\n" "cr0.ts =3\n" "seg_granularity =23\n" "vmfunc_view_sw_ctrl =0\n" "vmfunc_return_success =0\n" "untrusted_view_id =0\n" "vmcall_assert =42\n" "vmcall_ta_exception =0x9f\n" "size_core_state =30\n" // look at PER_CORE_STATE data structure "redirect_table =12\n" "sizeof_redirect_entry =16\n" "eresume_leaf =3\n" "eenter_opsize =2\n" "eenter_vector =29\n" "eresume_vector =30\n" "eexit_vector =31\n" "ss_rpl_mask =0x0003\n" "ss_rpl_ring3 =0x0003\n" "ss_ti_mask =0x0004\n" "ss_ti_gdt =0x0000\n" "ss_ti_ldt =0x0004\n" "enclave_crashed =0x1006\n" //error code "ecall_not_allowed =0x1007\n" //error code "exception_pf =14\n" "exception_last =31\n" "exception_nmi =2\n" "exception_mc =18\n" "view_0_chk_fail =0x2\n" "secs_scv_chk_fail =0x3\n" "trusted_view_chk_fail =0x4\n" "trusted_view_init_fail =0x5\n" "tcs_pg_align_chk_fail =0x6\n" "ssa_frame_avl_chk1_fail =0x7\n" "ssa_frame_avl_chk2_fail =0x8\n" "aep_chk_fail =0x9\n" "target_chk_fail =0xa\n" "ss_db_chk_fail =0xb\n" "ds_expand_chk_fail =0xc\n" "ossa_page_align_chk_fail=0xd\n" "ofsbase_page_align_chk_fail=0xe\n" "ogsbase_page_align_chk_fail=0xf\n" "no_fs_wrap_around_chk_fail=0x10\n" "fs_within_ds_chk_fail =0x11\n" "no_gs_wrap_around_chk_fail=0x12\n" "gs_within_ds_chk_fail =0x13\n" "tcs_reserved_chk_fail =0x14\n" "tcs_lock_acquire_fail =0x15\n" "test_code:\n" //itp_loop for debug "nop;\n" "itp_loop_exit_code:\n" "mov eax, 4;\n" "nop;\n" "nop;\n" "nop;\n" "nop;\n" "cmp eax, 5;\n" "jz itp_loop_exit_code;\n" "test_code_cpuindex:\n" "mov r13, patch_str_core_id;\n" //populate core id statically "mov r10, rdi;\n" //r10 == vector "mov r11, rsi;\n" //r11 == errorcode_flag //r12 == os_cr3 (initialized later) //rcx == view_handle //rdx == secs //check static ring-0 virtual address for SECS for current view //verify secret value on page to ensure it is not malicious "test_code_secs_patch1:\n" "mov rdx, patch_str_secs_ptr;\n" "mov rax, [rdx+secs.scv];\n" "test_code_cmp_patch:\n" "mov r9, patch_str_secs_scv;\n" "cmp rax, r9;\n" "jz exit_code_cookie_check_ok;\n" // For view0, cookie value check will fail when running // in EPT environment because we do not reveal the secure // cookie value to the untrusted view. "mov rbx, [rdx+secs.eid];\n" "cmp rbx, 0;\n" "jnz assert_invalid_secs;\n" //For unstrusted view, we compare the secs->scv with untrusted //scv "mov r9, patch_str_secs_scv_un;\n" "cmp rax, r9;\n" "jz exit_code_cookie_check_ok;\n" "assert_invalid_secs:\n" //VMCALL here to assert only for trusted view "mov eax, vmcall_assert;\n" "vmcall;\n" "exit_code_cookie_check_ok:\n" //if 0 do not switch views, pass control to os handler "mov rax, [rdx+secs.eid];\n" "cmp rax, 0;\n" "jz no_view_switch;\n" //we are in a trusted view //****************debug only start*************** /* "mov ebx, edx;\n" "add ebx, secs.vid;\n" "mov ax, 1;\n" //expect view 1 "mov cx, 0;\n" //switch vid to 0 ".byte 0xF0;\n" "cmpxchg [ebx], cx;\n" */ //****************debug only end*************** "mov eax, r10d;\n" "cmp eax, exception_last;\n" "jg continue_aex_flow;\n" // Do not crash TA for nmi and mc exceptions "cmp eax, exception_nmi;\n" "je continue_aex_flow;\n" // This is an exception in TA - mark SECS.attributes.inited = 0 "mov eax, [rdx+secs.attrib];\n" "and eax, ~0x1;\n" //Clear secs.attributes.inited "mov [rdx+secs.attrib], eax;\n" "continue_aex_flow:\n" //check boolean param whether to expect error code //on interrupt frame or not "mov eax, r11d;\n" "cmp eax, 0;\n" "jz no_error_code;\n" //lookup CS for ring3 dpl and eip from intr frame //if not r3 CS - this should not happen since we should not be in //non-0 view and in ring-0 CS, unless, we have ring-0 views in which //case we will need to save state for ring-0 code via r0 TCS //skip top 36 bytes of stack frame since we get called from MyHandler //which pushes eax, ebx, ecx, edx, esi, edi, vector, //errflag, retaddr (due to call) "mov eax, [rsp+if_skip_ec+if.cs];\n" //cs "mov rbx, [rsp+if_skip_ec+if.eip];\n" //interrupted eip "jmp continue_test;\n" "no_error_code:\n" "mov eax, [rsp+if_noskip_ec+if.cs];\n" //cs "mov rbx, [rsp+if_noskip_ec+if.eip];\n" //interrupted eip //we have cs and eip cached in eax and ebx resp. "continue_test:\n" //BUG: "mov eax, [esp+if_noskip_ec+if.cs];\n" //cs "and eax, ss_rpl_mask;\n" //BUG: "mov ebx, [esp+if_noskip_ec+if.eip];\n" //interrupted eip "cmp eax, ss_rpl_ring3;\n" //cmp with user/expected CS "jnz save_ret_noreplace;\n" //if not r3 CS dont touch intr frame /* //we have eip cached in ebx "mov eax, [edx+secs.base];\n" //get start of protected gla from secs "cmp ebx, eax;\n" //ensure EIP >= gla_start "jnae save_ret_noreplace;\n" //out_of_range if ebx !>= gla_start "add eax, [edx+secs.size];\n" //get start of protected gla from secs "cmp ebx, eax;\n" //ensure EIP <= gla_end "ja save_ret_noreplace;\n" //out of range if ebx > gla_end */ //we have to replace EIP on stack with AEP //and replace RSP on stack with external stack from TCS "jmp test_code_get_ssa;\n" "save_ret_noreplace:\n" "mov eax, vmcall_assert;\n" "vmcall;\n" "test_code_get_ssa:\n" //save original EIP (intr point) into SSA with RSP and other GPRS //all required gprs, esp, eip values should be on trusted stack //Establish ptr to TCS "xor rcx, rcx;\n" "mov rbx, rdx;\n" "add rbx, secs.pcd;\n" "mov ecx, r13d;\n" //core id cached in r13 "shl ecx, shift_size_secs_pcd;\n" "add rcx, rbx;\n" //ptr to core-specific pcd "mov ebx, [rcx+secs.pcd.tcs];\n" //Establish ptr to SSA frame (from TCS) //TCS is in ebx, OSSA, CSSA is a field in TCS struct //FIXME MAYBE - this could be done in EENTER, ERESUME //in which case this will require only one read from TCS "mov rcx, [ebx+tcs.ossa];\n" //ossa "mov rax, [rdx+secs.base];\n" //base "add rcx, rax;\n" //ssa_va = ossa+base "xor rax, rax;\n" // clear rax for new production "mov eax, [ebx+tcs.cssa];\n" //cssa "mov rsi, [rdx+secs.ssa_frame_size];\n" //ssa frame size "shl rsi, shift_size_4k;\n" //ssa frame size is in pages "mul rsi;\n" //size*cssa - result in rdx:rax "add rax, rcx;\n" //add base FIXME LATER higher 32 bits rdx ignored // XSAVE and clean //xsave registers on ssa starting from //the beginning. "fxsave64 [rax];\n" //Clear the fx registers by restoring value in them from //ssa +512 byte offset. "fxrstor64 [rax+512];\n" //gpr area at end of ssa page "add rax, 4096;\n" "sub rax, ssa_gpr_size;\n" //ensure order accessed on stack is same as saved from MyHandler "mov rcx, [rsp+start_gprs+gprs.eax];\n" //save eax "mov [rax+ssa.ax], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.ecx];\n" //save ecx "mov [rax+ssa.cx], ecx;\n" "mov rcx, [rsp+start_gprs+gprs.edx];\n" //save edx "mov [rax+ssa.dx], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.ebx];\n" //save ebx "mov [rax+ssa.bx], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.esi];\n" //save esi "mov [rax+ssa.si], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.edi];\n" //save edi "mov [rax+ssa.di], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r8];\n" //save r8 "mov [rax+ssa.r8], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r9];\n" //save r9 "mov [rax+ssa.r9], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r10];\n" //save r10 "mov [rax+ssa.r10], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r11];\n" //save r11 "mov [rax+ssa.r11], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r12];\n" //save r12 "mov [rax+ssa.r12], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r13];\n" //save r13 "mov [rax+ssa.r13], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r14];\n" //save r14 "mov [rax+ssa.r14], rcx;\n" "mov rcx, [rsp+start_gprs+gprs.r15];\n" //save r15 "mov [rax+ssa.r15], rcx;\n" "mov [rax+ssa.bp], rbp;\n" //save ebp directly since we dont clobber //Again, check boolean flag param whether to expect error code //on interrupt frame or not "mov ecx, r11d ;\n" "cmp ecx, 0;\n" "jz save_eflags_no_errcode;\n" "mov ecx, [rsp+if_skip_ec+if.eflags];\n" //save eflags after skipping error code "mov [rax+ssa.flags], ecx;\n" //FIXME LATER need to scrub eflags right "mov rcx, [rsp+if_skip_ec+if.eip];\n" //save eip "mov [rax+ssa.ip], rcx;\n" "mov rcx, [ebx+tcs.aep];\n" "mov [rsp+if_skip_ec+if.eip], rcx;\n" //replace eip with aep "mov rcx, [rsp+if_skip_ec+if.esp];\n" //save esp "mov [rax+ssa.sp], rcx;\n" "mov rcx, [eax+ssa.sp_u];\n" "mov [rsp+if_skip_ec+if.esp], rcx;\n" //replace esp with u_esp "jmp continue_ssa_save;\n" //TODO: remove the duplication and give skip/noskip by macro parameter "save_eflags_no_errcode:\n" "mov ecx, [rsp+if_noskip_ec+if.eflags];\n" //save eflags - no error code "mov [rax+ssa.flags], ecx;\n" "mov rcx, [rsp+if_noskip_ec+if.eip];\n" //save eip "mov [rax+ssa.ip], rcx;\n" "mov rcx, [ebx+tcs.aep];\n" "mov [rsp+if_noskip_ec+if.eip], rcx;\n" //replace eip with aep "mov rcx, [rsp+if_noskip_ec+if.esp];\n" //save esp "mov [rax+ssa.sp], rcx;\n" "mov rcx, [eax+ssa.sp_u];\n" "mov [rsp+if_noskip_ec+if.esp], rcx;\n" //replace esp with u_esp "continue_ssa_save:\n" //FIXME LATER - update exit_info struct at offset 160 //update CSSA in TCS "mov ecx, [ebx+tcs.cssa];\n" //cssa "inc ecx;\n" "mov [ebx+tcs.cssa], ecx;\n" //put synthetic state on stack for ESP=ESP_U, EBP=EBP_U and EIP=AEP_U //SWITCH stacks to RSP_U and copy interupt frame on RSP_u from RSP_t //add synthetic state for GPRs and add 8 for vector and flag so that //RSP_u is setup such that whether view switch happens or not //code following no_view_switch label always does the same thing: //pops passed params, and transfers control to MyHandler which //pops synthetic (or real values of GPRs) and rets to OS ISR via edx /* //bug no need to modify r0 stack - we have already modified esp on stack //we still need to scrub registers as done below... "mov esi, esp;\n" "mov esp, [eax+ssa.sp_u];\n" //[eax+144] references esp_u */ "mov rbp, [rax+ssa.bp_u];\n" //[eax+152] references ebp_u //-----No More trusted stack accesses after this point--------- //need to setup u_stack frame the same way as non_vs exit //push interrupt frame - aep and rsp have already been updated on frame //push synthetic gprs which will be popped by MyHandler code //push dummy vector, flag, MyHandler offset after call to this function //ebx has ptr to trusted esp "mov ecx, eresume_leaf;\n" "mov [rsp+start_gprs+gprs.eax], ecx;\n" //eax=eresume leaf "mov ecx, [ebx+tcs.aep];\n" "mov [rsp+start_gprs+gprs.ecx], ecx;\n" //ecx=AEP "mov ecx, 0;\n" "mov [rsp+start_gprs+gprs.edx], ecx;\n" //copy 0 edx "mov ecx, ebx;\n" "mov [rsp+start_gprs+gprs.ebx], ecx;\n" //ebx=TCS "mov rcx, 0;\n" "mov [rsp+start_gprs+gprs.r8], rcx;\n" //copy 0 r8 "mov [rsp+start_gprs+gprs.r9], rcx;\n" //copy 0 r9 "mov [rsp+start_gprs+gprs.r10], rcx;\n" //copy 0 r10 "mov [rsp+start_gprs+gprs.r11], rcx;\n" //copy 0 r11 "mov [rsp+start_gprs+gprs.r12], rcx;\n" //copy 0 r12 "mov [rsp+start_gprs+gprs.r13], rcx;\n" //copy 0 r13 "mov [rsp+start_gprs+gprs.r14], rcx;\n" //copy 0 r14 "mov [rsp+start_gprs+gprs.r15], rcx;\n" //copy 0 r15 // Check if the vector indicates an exception. // if (vector == exception) // Copy error code in gprs.esi // Copy faulting IP in gprs.edi // else // copy 0 to esi and 0 to edi "mov [rsp+start_gprs+gprs.esi], rcx;\n" //copy 0 esi "mov [rsp+start_gprs+gprs.edi], rcx;\n" //copy 0 edi "mov esi, r10d;\n" "cmp esi, exception_last;\n" "jg no_trusted_view_exception;\n" "cmp esi, exception_nmi;\n" "je no_trusted_view_exception;\n" "mov ecx, enclave_crashed;\n" "mov [rsp+start_gprs+gprs.esi], ecx;\n" //copy error code to esi //TODO: Make sure that faulting ip register size is consistent with expected //value by the sdk "mov ecx, [eax+ssa.ip];\n" "mov [rsp+start_gprs+gprs.edi], ecx;\n" //copy faulting address to edi "mov [rsp+start_gprs+gprs.edx], esi;\n" // Copy error code to edx "no_trusted_view_exception:\n" //STACKFIX Ravi - at this point the stack frame is ready to be used //### get address of exit page "test_code_exit_page_patch:\n" "mov rax, patch_str_exit_page;\n" "mov r15, rax;\n" //### bulk copy stack frame from trusted r0 stack to exit-page "pop rax;\n" //caller rip "mov [r15], rax;\n" "pop rax;\n" //rax "mov [r15+start_gprs+gprs.eax], rax;\n" "pop rax;\n" //rcx "mov [r15+start_gprs+gprs.ecx], rax;\n" "pop rax;\n" //rdx "mov [r15+start_gprs+gprs.edx], rax;\n" "pop rax;\n" //rbx "mov [r15+start_gprs+gprs.ebx], rax;\n" "pop rax;\n" //rsi "mov [r15+start_gprs+gprs.esi], rax;\n" "pop rax;\n" //rdi "mov [r15+start_gprs+gprs.edi], rax;\n" "pop rax;\n" //r15 "mov [r15+start_gprs+gprs.r15], rax;\n" "pop rax;\n" //r14 "mov [r15+start_gprs+gprs.r14], rax;\n" "pop rax;\n" //r13 "mov [r15+start_gprs+gprs.r13], rax;\n" "pop rax;\n" //r12 "mov [r15+start_gprs+gprs.r12], rax;\n" "pop rax;\n" //r11 "mov [r15+start_gprs+gprs.r11], rax;\n" "pop rax;\n" //r10 "mov [r15+start_gprs+gprs.r10], rax;\n" "pop rax;\n" //r9 "mov [r15+start_gprs+gprs.r9], rax;\n" "pop rax;\n" //r8 "mov [r15+start_gprs+gprs.r8], rax;\n" //### check for err code "mov eax, r11d;\n" "cmp eax, 0;\n" "jz no_err_code_to_copy;\n" "pop rax;\n" //errcode "mov [r15+if_noskip_ec+if.errcode], rax;\n" "pop rax;\n"//eip "mov [r15+if_skip_ec+if.eip], rax;\n" //eip "pop rax;\n" //cs "mov [r15+if_skip_ec+if.cs], rax;\n" "pop rax;\n" //rflags "mov [r15+if_skip_ec+if.eflags], rax;\n" "pop rax;\n" //rsp "mov [r15+if_skip_ec+if.esp], rax;\n" "pop rax;\n" //ss "mov [r15+if_skip_ec+if.ss], rax;\n" "jmp continue_with_copy;\n" "no_err_code_to_copy:\n" "pop rax;\n" //eip "mov [r15+if_noskip_ec+if.eip], rax;\n" "pop rax;\n" //cs "mov [r15+if_noskip_ec+if.cs], rax;\n" "pop rax;\n" //rflags "mov [r15+if_noskip_ec+if.eflags], rax;\n" "pop rax;\n" //rsp "mov [r15+if_noskip_ec+if.esp], rax;\n" "pop rax;\n" //ss "mov [r15+if_noskip_ec+if.ss], rax;\n" "continue_with_copy:\n" //#### adjust untrusted rsp to remove frame - already done above in pops //#### (do we need to - will always be used from tss->rsp0) //#### cache untrusted r0 stack ptr from TCS?? into r14 "mov r14, [ebx+tcs.ur0sp];\n" //FIXME? how come we are accessing TCS after cr3 switched back?? //cr3 switch should be right before vmfunc out //Make TCS state inactive //lock cmpxchg tcs.state from expect_active to set_inactive "mov eax, tcs_state_expect_active;\n" "mov ecx, tcs_state_set_inactive;\n" ".byte 0xF0;\n" "cmpxchg [ebx], ecx;\n" "je tcs_state_inactive_ok;\n" "mov eax, vmcall_assert;\n" "vmcall;\n" "tcs_state_inactive_ok:\n" //we need to save the FS, GS base and limit on EENTER into TCS //we access gdt and ldt from a SECS cached location cached during creation //so that we dont have to perform SGDT again (also exit will occur) //FIXME LATER - assumes FS, GS is always pointing to GDT and not LDT //r13d has core id cached, esi is available "test_code_secs_patch2:\n" "mov rdx, patch_str_secs_ptr;\n" //since edx was clobbered in the mul above "mov rsi, rdx;\n" "add rsi, secs.pcd;\n" "mov rcx, r13;\n" //we need to cache core id in r13 "shl ecx, shift_size_secs_pcd;\n" "add rsi, rcx;\n" //get ptr to core-specific pcd //swap fs "mov rax, [rsi+secs.pcd.gdtr];\n" "xor rcx, rcx;\n" "mov cx, [ebx+tcs.save_fs_selector];\n" "mov edx, ecx;\n" "and ecx, 0xFFF8;\n" //shift TI and RPL fields out and mul by 8 "and edx, 4;\n" //fs.TI "cmp edx, 0;\n" "je cont_fs_restore_aex;\n" "mov rax, [rsi+secs.pcd.ldtr];\n" //restore in LDT "cont_fs_restore_aex:\n" "add rax, rcx;\n" "mov ecx, [ebx+tcs.save_fs_desc_low];\n" "mov [rax], ecx;\n" "mov ecx, [ebx+tcs.save_fs_desc_high];\n" "mov [rax+4], ecx;\n" "xor rcx, rcx;\n" "mov cx, [ebx+tcs.save_fs_selector];\n" "mov fs, ecx;\n" //swap gs "mov rax, [rsi+secs.pcd.gdtr];\n" "xor rcx, rcx;\n" "mov cx, [ebx+tcs.save_gs_selector];\n" "mov edx, ecx;\n" "shr ecx, 3;\n" //shift TI and RPL fields out "shl ecx, 3;\n" //selector x 8 (bytes) "and edx, 4;\n" //gs.TI "cmp edx, 0;\n" "je cont_gs_restore_aex;\n" "mov rax, [rsi+secs.pcd.ldtr];\n" //restore GS in LDT "cont_gs_restore_aex:\n" "add rax, rcx;\n" "mov ecx, [ebx+tcs.save_gs_desc_low];\n" "mov [rax], ecx;\n" "mov ecx, [ebx+tcs.save_gs_desc_high];\n" "mov [rax+4], ecx;\n" "xor rcx, rcx;\n" "mov cx, [ebx+tcs.save_gs_selector];\n" "mov gs, ecx;\n" //-----No More TCS accesses after this point--------- #ifdef ACTIVATE_ACR3 //switch CR3 to CR3_u from TCS "mov rax, [ebx+tcs.os_cr3];\n" "mov cr3, rax;\n" #endif //vmfunc to view 0 always (exit) - this could be on a seperate page "mov rax, vmfunc_view_sw_ctrl;\n" "mov rcx, untrusted_view_id;\n" //"vmcall;\n" //VMCALL only for debug #if 1 //Assembler on our dev machine does not support //vmfunc instruction - so writing byte code sequence ".byte 0x0f;\n" ".byte 0x01;\n" ".byte 0xd4;\n" #else "vmfunc;\n" #endif //STACKFIX Ravi - at this point stack frame is copied to exit-page r15 //### copy stack frame from exit-page to untrusted r0 stack (r14) //### alternately tss has remapped to os original so we could also read tss->esp0 to use //### pivot stack to untrusted stack cached in r14 "mov rsp, r14;\n" //### check for err code "mov eax, r11d;\n" "cmp eax, 0;\n" "jz no_err_code_to_copy_2;\n" "mov rax, [r15+if_skip_ec+if.ss];\n" "push rax;\n" //ss "mov rax, [r15+if_skip_ec+if.esp];\n" "push rax;\n" //rsp "mov rax, [r15+if_skip_ec+if.eflags];\n" "push rax;\n" //rflags "mov rax, [r15+if_skip_ec+if.cs];\n" "push rax;\n" //cs "mov rax, [r15+if_skip_ec+if.eip];\n" //eip "push rax;\n"//eip "mov rax, [r15+if_noskip_ec+if.errcode];\n" "push rax;\n" //errcode "jmp continue_with_copy_2;\n" "no_err_code_to_copy_2:\n" "mov rax, [r15+if_noskip_ec+if.ss];\n" "push rax;\n" //ss "mov rax, [r15+if_noskip_ec+if.esp];\n" "push rax;\n" //rsp "mov rax, [r15+if_noskip_ec+if.eflags];\n" "push rax;\n" //rflags "mov rax, [r15+if_noskip_ec+if.cs];\n" "push rax;\n" //cs "mov rax, [r15+if_noskip_ec+if.eip];\n" //eip "push rax;\n"//eip "continue_with_copy_2:\n" "mov rax, [r15+start_gprs+gprs.r8];\n" "push rax;\n" //r8 "mov rax, [r15+start_gprs+gprs.r9];\n" "push rax;\n" //r9 "mov rax, [r15+start_gprs+gprs.r10];\n" "push rax;\n" //r10 "mov rax, [r15+start_gprs+gprs.r11];\n" "push rax;\n" //r11 "mov rax, [r15+start_gprs+gprs.r12];\n" "push rax;\n" //r12 "mov rax, [r15+start_gprs+gprs.r13];\n" "push rax;\n" //r13 "mov rax, [r15+start_gprs+gprs.r14];\n" "push rax;\n" //r14 "mov rax, [r15+start_gprs+gprs.r15];\n" "push rax;\n" //r15 "mov rax, [r15+start_gprs+gprs.edi];\n" "push rax;\n" //rdi "mov rax, [r15+start_gprs+gprs.esi];\n" "push rax;\n" //rsi "mov rax, [r15+start_gprs+gprs.ebx];\n" "push rax;\n" //rbx "mov rax, [r15+start_gprs+gprs.edx];\n" "push rax;\n" //rdx "mov rax, [r15+start_gprs+gprs.ecx];\n" "push rax;\n" //rcx "mov rax, [r15+start_gprs+gprs.eax];\n" "push rax;\n" //rax "mov rax, [r15];\n" "push rax;\n" //caller rip //if (vector == to_be_handled_exception) //else, jmp no_view_switch "mov esi, r10d;\n" "cmp esi, exception_last;\n" "jg after_view_switch;\n" // No TA exception handling "cmp esi, exception_nmi;\n" "je no_view_switch;\n" "cmp esi, exception_mc;\n" "je after_view_switch;\n" "jmp exit_ta_exception;\n" "after_view_switch:\n" "no_view_switch:\n" //if no view switch, no cr3 or stack switch either //get redirect base address for this core "test_code_ptr_core_state_patch:\n" "mov rbx, patch_str_core_state_ptr;\n" "xor rax, rax;\n" "xor rcx, rcx;\n" "mov eax, r13d;\n" //patch core-id during vidt setup "mov ecx, size_core_state;\n" "mul ecx;\n" // *** edx <- high order bits, but multiplication is small. *** "add rbx, rax;\n" //get ptr to per core state - retain in ebx //fetch OS ISR address and cache in r8 "mov r9, [rbx + redirect_table];\n" //get redirect base address for this core "mov ecx, r10d;\n" //ecx = vector "mov eax, sizeof_redirect_entry;\n" "mul ecx;\n" // eax contains the multiplied output, edx is ignored as multiplication is small "mov r8, [rax+r9];\n" //r8 = os isr address "mov eax, 1;\n" //to indicate to MyHandler to use ret "ret;\n" //pop errflag and vector passed by MyHandler entry code //NOTE edi has the os original handler address //MyHandler will xchg edi with TOS and ret to kernel ISR cleanly //Ravi - keep this block - it us unused for now //Ravi - it is used to check result after pf to add page to acr3 //special exit to not touch MyHandler ISR - to be able //to conditionally pop error code for events like PF!! "exit_ta_exception:\n" "cmp r11d, 0;\n" "je exit_no_error_code_pop;\n" "pop rax;\n" // pop vidt stub rip "pop rax;\n" "pop rcx;\n" "pop rdx;\n" "pop rbx;\n" "pop rsi;\n" "pop rdi;\n" "pop r15;\n" "pop r14;\n" "pop r13;\n" "pop r12;\n" "pop r11;\n" "pop r10;\n" "pop r9;\n" "pop r8;\n" "add rsp, 8;\n" //pop error code without killing gprs! "iretq;\n" "exit_no_error_code_pop:\n" "pop rax;" //pop vidt stub rip "pop rax;\n" "pop rcx;\n" "pop rdx;\n" "pop rbx;\n" "pop rsi;\n" "pop rdi;\n" "pop r15;\n" "pop r14;\n" "pop r13;\n" "pop r12;\n" "pop r11;\n" "pop r10;\n" "pop r9;\n" "pop r8;\n" "iretq;\n" ".att_syntax\n" ); void test_code_end(void); __asm__ ( ".globl test_code_end\n" "test_code_end:" "nop;\n" ); //EEXIT VIEW FLOW //NOTE - expected to be called only from MyHandler code //Expects to get a flag param on stack to skip error code //based on IDT vector - some entries below 20 have error codei //This exit flow is performed through a ring-0 trampoline memory pages //referenced via the asserted page table setup for the Trusted View by //the initialization flow. This flow is initiated by software executing //inside the TV using an INT opcode that transfers control to the //trusted ring-0 trampoline code. //Pre-conditions: //Executing inside in trusted view //GPRs passed in are the same as EEXIT (RAX (in) 04h, RBX (in) holds //the address to branch outside the trusted view, RCX (out) returns AEP) //Note - Responsibility of tRTS software to clear out GPR state and swap //stack to OS-external stack from TCS and then invoke this flow via INT void exit_code_cpuindex(void); void exit_code_cmp_patch(void); void exit_code_secs_patch1(void); void exit_code_exit_page_patch(void); void exit_code(void); __asm__ ( ".intel_syntax noprefix\n" ".globl exit_code_cpuindex\n" ".globl exit_code\n" ".globl exit_code_cmp_patch\n" ".globl exit_code_secs_patch1\n" ".globl exit_code_exit_page_patch\n" //NOTE WELL - this routine uses all the defines from //test_code (async exit flow) "exit_code:\n" //itp_loop for debug "nop\n" "exit_code_itp_loop:\n" "mov eax, 3;\n" "nop;\n" "nop;\n" "nop;\n" "nop;\n" "cmp eax, 5;\n" "jz exit_code_itp_loop;\n" "exit_code_step_1:\n" //check static ring-0 virtual address for SECS for current view //verify secret value on page to ensure it is not malicious "exit_code_secs_patch1:\n" "mov rdx, patch_str_secs_ptr;\n" "mov r10, rdi;\n" //r10 == vector "mov r11, rsi;\n" //r11 == errorcode_flag "mov rax, [rdx+secs.scv];\n" "exit_code_cmp_patch:\n" "mov r9, patch_str_secs_scv;\n" "cmp rax, r9;\n" "jz exit_code_step_2;\n" //VMCALL here to assert "mov eax, vmcall_assert;\n" "vmcall;\n" "exit_code_step_2:\n" //if 0 then assert "mov rax, [rdx+secs.eid];\n" "cmp rax, 0;\n" "jne exit_code_step_3;\n" //VMCALL here to assert "mov eax, vmcall_assert;\n" "vmcall;\n" //we are in a trusted view "exit_code_step_3:\n" //****************debug only start*************** /* "mov ebx, edx;\n" "add ebx, secs.vid;\n" "mov ax, 1;\n" //expect view 1 "mov cx, 0;\n" //switch vid to 0 ".byte 0xF0;\n" "cmpxchg [ebx], cx;\n" */ //****************debug only end*************** //ignore boolean param whether to expect error code //on interrupt frame or not - since we know this is a sw int flow //lookup CS for ring3 dpl and eip from intr frame //if not r3 CS - this should not happen since we should not be in //non-0 view and in ring-0 CS, unless, we have ring-0 views "mov rax, [rsp+if_noskip_ec+if.cs];\n" //cs "and rax, ss_rpl_mask;\n" "mov rbx, [rsp+if_noskip_ec+if.eip];\n" //interrupted eip "cmp rax, ss_rpl_ring3;\n" //cmp with user/expected CS "jz exit_code_step_4;\n" //if not r3 CS dont touch intr frame "mov eax, vmcall_assert;\n" "vmcall;\n" "exit_code_step_4:\n" //we have eip cached in ebx "mov rax, [rdx+secs.base];\n" //get start of protected gla from secs "cmp rbx, rax;\n" //ensure EIP >= gla_start "jae exit_code_step_4b;\n" "mov eax, vmcall_assert;\n" "vmcall;\n" "exit_code_step_4b:\n" "add rax, [rdx+secs.size];\n" //get start of protected gla from secs "cmp rbx, rax;\n" //ensure EIP <= gla_end "jbe exit_code_step_5;\n" "mov eax, vmcall_assert;\n" "vmcall;\n" "exit_code_step_5:\n" //we have to replace EIP on stack with RBX (desired target) //Establish ptr to TCS to read AEP (we need to pass it back in RCX) "mov rbx, rdx;\n" "add rbx, secs.pcd;\n" "exit_code_cpuindex:\n" "mov rdi, patch_str_core_id;\n" //populate core id statically "shl rdi, shift_size_secs_pcd;\n" "add rdi, rbx;\n" //ptr to core-specific pcd "mov rbx, [rdi+secs.pcd.tcs];\n" //setup RCX to contain AEP, fills ecx on untrusted stack setup //by MyHandler "mov rcx, [rbx+tcs.aep];\n" "mov [rsp+start_gprs+gprs.ecx], rcx;\n" //ecx=AEP //cache os-cr3 from tcs into gpr FIXME LATER "mov rsi, [rbx+tcs.os_cr3];\n" //Make TCS state inactive //lock cmpxchg tcs.state from expect_active to set_inactive "mov rax, tcs_state_expect_active;\n" "mov rcx, tcs_state_set_inactive;\n" ".byte 0xF0;\n" "cmpxchg [rbx], rcx;\n" "je exit_code_step_6;\n" "mov eax, vmcall_assert;\n" "vmcall;\n" "exit_code_step_6:\n" //we need to save the FS, GS base and limit on EENTER into TCS //we access gdt and ldt from a SECS cached location cached during creation //so that we dont have to perform SGDT again (also exit will occur) //FIXME LATER - assumes FS, GS is always pointing to GDT and not LDT //swap fs //edi has core-specific pcd "mov rax, [rdi+secs.pcd.gdtr];\n" //gdtr in eax "xor rcx, rcx;\n" "mov cx, [rbx+tcs.save_fs_selector];\n" "mov edx, ecx;\n" "and rcx, 0xFFF8;\n" //shift TI and RPL fields out and mul by 8 "and edx, 4;\n" //fs.TI "cmp edx, 0;\n" "je cont_fs_restore_exit;\n" "mov rax,[rdi+secs.pcd.ldtr];\n" //restore in LDT "cont_fs_restore_exit:\n" "add rax, rcx;\n" "mov rcx, [rbx+tcs.save_fs_desc_low];\n" "mov [rax], rcx;\n" "mov rcx, [rbx+tcs.save_fs_desc_high];\n" "mov [rax+4], rcx;\n" "xor rcx, rcx;\n" "mov cx, [rbx+tcs.save_fs_selector];\n" "mov fs, cx;\n" //swap gs "mov rax, [rdi+secs.pcd.gdtr];\n" //gdtr in eax "xor rcx, rcx;\n" "mov cx, [rbx+tcs.save_gs_selector];\n" "mov edx, ecx;\n" "shr rcx, 3;\n" //shift TI and RPL fields out "shl rcx, 3;\n" //selector x 8 (bytes) "and edx, 4;\n" //gs.TI "cmp edx, 0;\n" "je cont_gs_restore_exit;\n" "mov rax,[rdi+secs.pcd.ldtr];\n" //restore gs in LDT "cont_gs_restore_exit:\n" "add rax, rcx;\n" "mov rcx, [rbx+tcs.save_gs_desc_low];\n" "mov [rax], rcx;\n" "mov rcx, [rbx+tcs.save_gs_desc_high];\n" "mov [rax+4], rcx;\n" "xor ecx, ecx;\n" "mov cx, [ebx+tcs.save_gs_selector];\n" "mov gs, ecx;\n" //set desired target for iret "mov rcx, [rsp+start_gprs+gprs.ebx];\n" //tRTS passed desired target in ebx "mov [rsp+if_noskip_ec+if.eip], rcx;\n" //replace eip with desired target //rsp on stack should already by untrusted stack (switched by tRTS) //STACKFIX Ravi - we need to move the bulk frame to exit-page //### get location of exit-page "exit_code_exit_page_patch:\n" "mov rax, patch_str_exit_page;\n" "mov r15, rax;\n" //### bulk copy stack frame from trusted r0 stack to exit-page "pop rax;\n" //caller rip "mov [r15], rax;\n" "pop rax;\n" //rax "mov [r15+start_gprs+gprs.eax], rax;\n" "pop rax;\n" //rcx "mov [r15+start_gprs+gprs.ecx], rax;\n" "pop rax;\n" //rdx "mov [r15+start_gprs+gprs.edx], rax;\n" "pop rax;\n" //rbx "mov [r15+start_gprs+gprs.ebx], rax;\n" "pop rax;\n" //rsi "mov [r15+start_gprs+gprs.esi], rax;\n" "pop rax;\n" //rdi "mov [r15+start_gprs+gprs.edi], rax;\n" "pop rax;\n" //r15 "mov [r15+start_gprs+gprs.r15], rax;\n" "pop rax;\n" //r14 "mov [r15+start_gprs+gprs.r14], rax;\n" "pop rax;\n" //r13 "mov [r15+start_gprs+gprs.r13], rax;\n" "pop rax;\n" //r12 "mov [r15+start_gprs+gprs.r12], rax;\n" "pop rax;\n" //r11 "mov [r15+start_gprs+gprs.r11], rax;\n" "pop rax;\n" //r10 "mov [r15+start_gprs+gprs.r10], rax;\n" "pop rax;\n" //r9 "mov [r15+start_gprs+gprs.r9], rax;\n" "pop rax;\n" //r8 "mov [r15+start_gprs+gprs.r8], rax;\n" "pop rax;\n" //eip "mov [r15+if_noskip_ec+if.eip], rax;\n" "pop rax;\n" //cs "mov [r15+if_noskip_ec+if.cs], rax;\n" "pop rax;\n" //rflags "mov [r15+if_noskip_ec+if.eflags], rax;\n" "pop rax;\n" //rsp "mov [r15+if_noskip_ec+if.esp], rax;\n" "pop rax;\n" //ss "mov [r15+if_noskip_ec+if.ss], rax;\n" //### adjust trusted r0 stack ptr - done with pops //### (not needed since will be used from tss automatically) //### cache untrusted r0 stack ptr from TCS into r14 "mov r14, [rbx+tcs.ur0sp];\n" //FIXME noticed that tcs is accessed with a rbx base in this routine //and with a ebx base in other routines - should fix //vmfunc to view 0 always (exit) - this could be on a seperate page "mov rax, vmfunc_view_sw_ctrl;\n" "mov rcx, untrusted_view_id;\n" #ifdef ACTIVATE_ACR3 //switch cr3 to value cached in gpr - note - imp to do this before //view switched - since acr3 is only mapped in trusted view so //if you switch cr3 afterwards, that code will never run after vmfunc //to untrusted view (since your cr3 is invalid!) "mov cr3, rsi;\n" #endif #if 1 ".byte 0x0f;\n" ".byte 0x01;\n" ".byte 0xd4;\n" #else "vmfunc;\n" //VMCALL only for debug #endif //FIXME TODO Should add a check here that ecx has untrusted view id (0) //to disallow malicious code from jmping to the vmfunc and misusing trampoline page //STACKFIX Ravi - at this point stack frame is copied to exit-page r15 //### copy stack frame from exit-page to untrusted r0 stack (r14) //### alternately tss has remapped to os original so we could also read tss->esp0 to use //### pivot stack to untrusted stack cached in r14 "mov rsp, r14;\n" "mov rax, [r15+if_noskip_ec+if.ss];\n" "push rax;\n" //ss "mov rax, [r15+if_noskip_ec+if.esp];\n" "push rax;\n" //rsp "mov rax, [r15+if_noskip_ec+if.eflags];\n" "push rax;\n" //rflags "mov rax, [r15+if_noskip_ec+if.cs];\n" "push rax;\n" //cs "mov rax, [r15+if_noskip_ec+if.eip];\n" //eip "push rax;\n"//eip "mov rax, [r15+start_gprs+gprs.r8];\n" "push rax;\n" //r8 "mov rax, [r15+start_gprs+gprs.r9];\n" "push rax;\n" //r9 "mov rax, [r15+start_gprs+gprs.r10];\n" "push rax;\n" //r10 "mov rax, [r15+start_gprs+gprs.r11];\n" "push rax;\n" //r11 "mov rax, [r15+start_gprs+gprs.r12];\n" "push rax;\n" //r12 "mov rax, [r15+start_gprs+gprs.r13];\n" "push rax;\n" //r13 "mov rax, [r15+start_gprs+gprs.r14];\n" "push rax;\n" //r14 "mov rax, [r15+start_gprs+gprs.r15];\n" "push rax;\n" //r15 "mov rax, [r15+start_gprs+gprs.edi];\n" "push rax;\n" //rdi "mov rax, [r15+start_gprs+gprs.esi];\n" "push rax;\n" //rsi "mov rax, [r15+start_gprs+gprs.ebx];\n" "push rax;\n" //rbx "mov rax, [r15+start_gprs+gprs.edx];\n" "push rax;\n" //rdx "mov rax, [r15+start_gprs+gprs.ecx];\n" "push rax;\n" //rcx "mov rax, [r15+start_gprs+gprs.eax];\n" "push rax;\n" //rax "mov rax, [r15];\n" "push rax;\n" //caller rip "mov rax, 0;\n" //return flag to indicate use IRET not RET "ret;\n" //We return a flag in eax to MyHandler so that it checks that //and either does a RET to the OS handler (like AEX) to address in edi OR //IRETs for our INT flows, in either case, MyHandler leaves only the //required intr frame on the appropriate stack before issueing IRET or RET //IRET cleans up the intr frame, and RET does not (as intended) ".att_syntax\n" ); void exit_code_end(void); __asm__ ( ".globl exit_code_end\n" "exit_code_end:" "nop;\n" ); // this is a "function" that tests if the base of segment whose // selector is in eax is 0; it assumes gdt is in rsi and ldt in rdi // it leaves copies of the segment.lo in edx and hi in ecxi #define CHECK_SEGMENT_BASE(seg) \ "mov edx, eax;\n" /*make a copy*/ \ "shr eax, 3;\n" /*eax has index*/ \ "and edx, 4;\n" /*edx has table type*/ \ \ "cmp edx, 0;\n" /*if GDT*/ \ "jne use_ldt_"seg";\n" \ "mov ecx, [rsi+rax*8];\n" \ "mov eax, [rsi+rax*8+4];\n" \ "jmp dt_used_"seg";\n" \ "use_ldt_"seg":\n" \ "mov ecx, [rdi+rax*8];\n" \ "mov eax, [rdi+rax*8+4];\n" \ "dt_used_"seg":\n" \ \ "mov edx, ecx;\n" /*make a copy we need it later*/ \ "and ecx, 0xFFFF0000;\n" /*now check the 3 base fields*/ \ "cmp ecx, 0;\n" /*ecx has lo 32 bits*/ \ "jz base_address_15_00_check_ok_"seg";\n" \ "jmp gp_vmcall;\n" \ \ "base_address_15_00_check_ok_"seg":\n" \ "mov ecx, eax;\n" /*eax has hi 32 bits*/ \ "and eax, 0xFF0000FF;\n" \ "cmp eax, 0;\n" \ "jz base_address_31_16_check_ok_"seg";\n" \ \ "jmp gp_vmcall;\n" \ \ "base_address_31_16_check_ok_"seg":\n" \ "nop;\n" \ "nop;\n" void enter_eresume_code_cpuindex(void); void enter_eresume_code_cmp_patch1(void); void enter_eresume_code_cmp_patch2(void); void enter_eresume_code_secs_patch1(void); void enter_eresume_code_secs_patch2(void); void enter_eresume_code_secs_patch3(void); void enter_eresume_code_secs_patch4(void); void enter_eresume_code_secs_patch5(void); void enter_eresume_code_secs_patch6(void); void enter_eresume_code_enter_page_patch(void); void enter_eresume_code(void); __asm__ ( ".intel_syntax noprefix\n" ".globl enter_eresume_code_cpuindex\n" ".globl enter_eresume_code\n" ".globl enter_eresume_code_secs_patch1\n" ".globl enter_eresume_code_secs_patch2\n" ".globl enter_eresume_code_secs_patch1\n" ".globl enter_eresume_code_secs_patch2\n" ".globl enter_eresume_code_secs_patch3\n" ".globl enter_eresume_code_secs_patch4\n" ".globl enter_eresume_code_secs_patch5\n" ".globl enter_eresume_code_secs_patch6\n" ".globl enter_eresume_code_cmp_patch1\n" ".globl enter_eresume_code_cmp_patch2\n" ".globl enter_eresume_code_enter_page_patch\n" //NOTE WELL - this routine uses all the defines from //test_code (async exit flow) //#if 0 "enter_eresume_code:\n" "enter_eresume_code_itp_loop:\n" "mov eax, 2;\n" "nop;\n" "nop;\n" "nop;\n" "nop;\n" "cmp eax, 3;\n" "jz enter_eresume_code_itp_loop;\n" "enter_eresume_code_step_1:\n" //We are entering this code through an //interrupt gate then we dont need to disable interrupts //STACKFIX Ravi - copy stack contents from untrusted (unknown) stack //to pre known (untrusted) enter page buffer //#### get enter-page for cpu "enter_eresume_code_enter_page_patch:\n" "mov rax, patch_str_enter_page;\n" //#### cache enter-page address in r15 "mov r15, rax;\n" //r15 == enter_page //#### COPY STACK contents in bulk (whole stack frame) to enter-page "pop rax;\n" //rip "mov [r15], rax;\n" "pop rax;\n" //rax "mov [r15+start_gprs+gprs.eax], rax;\n" "pop rax;\n" //rcx "mov [r15+start_gprs+gprs.ecx], rax;\n" "pop rax;\n" //rdx "mov [r15+start_gprs+gprs.edx], rax;\n" "pop rax;\n" //rbx "mov [r15+start_gprs+gprs.ebx], rax;\n" "pop rax;\n" //rsi "mov [r15+start_gprs+gprs.esi], rax;\n" "pop rax;\n" //rdi "mov [r15+start_gprs+gprs.edi], rax;\n" "pop rax;\n" //r15 "mov [r15+start_gprs+gprs.r15], rax;\n" "pop rax;\n" //r14 "mov [r15+start_gprs+gprs.r14], rax;\n" "pop rax;\n" //r13 "mov [r15+start_gprs+gprs.r13], rax;\n" "pop rax;\n" //r12 "mov [r15+start_gprs+gprs.r12], rax;\n" "pop rax;\n" //r11 "mov [r15+start_gprs+gprs.r11], rax;\n" "pop rax;\n" //r10 "mov [r15+start_gprs+gprs.r10], rax;\n" "pop rax;\n" //r9 "mov [r15+start_gprs+gprs.r9], rax;\n" "pop rax;\n" //r8 "mov [r15+start_gprs+gprs.r8], rax;\n" "pop rax;\n" //rip "mov [r15+if_noskip_ec+if.eip], rax;\n" "pop rax;\n" //cs "mov [r15+if_noskip_ec+if.cs], rax;\n" "pop rax;\n" //rflags "mov [r15+if_noskip_ec+if.eflags], rax;\n" "pop rax;\n" //rsp "mov [r15+if_noskip_ec+if.esp], rax;\n" "pop rax;\n" //ss "mov [r15+if_noskip_ec+if.ss], rax;\n" //#### adjust untrusted rsp to remove frame - already done above in pops //#### cache untrusted r0 stack in r14 since after we switch tss will be //remapped and we will need to save this - should go into secs pcd OR TCS?? "mov r14, rsp;\n" //View handle passed in via RDX, load into RCX "enter_eresume_code_cpuindex:\n" "mov rax, patch_str_core_id;\n" "mov r13, rax;\n" //r13 == core_id "mov r10, rdi;\n" //r10 == vector "mov r11, rsi;\n" //r11 == errorcode_flag //r12 == os_cr3 (initialized later) //rcx == view_handle //rdx == secs //this stack frame is copied over to enter-page at this point: //+------------+ //| SS | 152 //+------------+ //| RSP | 144 //+------------+ //| RFLAGS | 136 //+------------+ //| CS | 128 //+------------+ //| RIP | 120 //+------------+ //| R8 | 112 //+------------+ //| R9 | 104 //+------------+ //| R10 | 96 //+------------+ //| R11 | 88 //+------------+ //| R12 | 80 //+------------+ //| R13 | 72 //+------------+ //| R14 | 64 //+------------+ //| R15 | 56 //+------------+ //| RDI | 48 //+------------+ //| RSI | 40 //+------------+ //| RBX | 32 //+------------+ //| RDX | 24 //+------------+ //| RCX | 16 //+------------+ //| RAX | 8 //+------------+ //| RIP (CALL) | 0 //+------------+ // my assumption is that eenter/eresume flow is called from myhandler // if that's the case, I alrady have esp //Save GPRS (RCX specifically) as passed in by EENTER since RCX is used to switch views //View handle passed in via RDX, load into RCX for EPTP switching "mov rcx, rdx;\n" // edx is now available //Check that the Current View is zero (entry should be //invoked by untrusted code) "enter_eresume_code_secs_patch1:\n" "mov rdx, patch_str_secs_ptr;\n" "mov rax, [rdx+secs.scv];\n" "enter_eresume_code_cmp_patch1:\n" "mov r9, patch_str_secs_scv_un;\n" "cmp rax, r9;\n" "jz view_0_secs_check_ok;\n" "mov ecx, 0x1;\n" "jmp gp_vmcall;\n" "view_0_secs_check_ok:\n" #if 0 "mov r9, 0xDEADBEEFDEADBEEF;\n" "mov [rdx+secs.scv], r9;\n" #endif //KCZ must be 0; otherwise, EENTER is illegal "mov rax, [rdx+secs.eid];\n" "cmp rax, 0;\n" "jz view_0_check_ok;\n" "mov ecx, view_0_chk_fail;\n" "jmp gp_vmcall;\n" "view_0_check_ok:\n" //VMM sets/resets the ept_enabled flag based on the //presence or absence of active views. If EPT is disabled. //then we should not try to do vmfunc. "mov rax, [rdx+secs.ept_enabled];\n" "bt rax, 0;\n" "jnc cancel_enter_no_view_switch;\n" // Raise NM# exception before // view switch, so that we never get "Device // not available" exception in trusted code. // fwait causes an NM# if CR0.TS flag is set. // The flag is set by the OS to delay the restoring // of FPU context on a context switch. Only when the // FPU instruction (like fwait, or wait) is execute, // the NM# exception in raised on which the OS resets // CR0.ts, thereby preventing further NM# "mov rax, cr0;\n" "bt rax, cr0.ts;\n" "jnc do_vmfunc;\n" "swapgs;\n" //Switch to kernel GS, as fwait will raise an exception "fwait;\n" // Will raise NM# exception "swapgs;\n" //Switch back to user GS, as the enter flow assumes that // gs points to user gs "do_vmfunc:\n" //#endif //****************debug only start*************** /* //VIVEK: update secs_ptr->base with address passed in eax "mov eax, [esp+12];\n" //get vector "cmp eax, eresume_vector;\n" "je eresume_flow;\n" //do this only for enter flow (since for resume it should //already be set correctly) "mov eax, [esp+start_gprs+gprs.eax +4];\n" // eax in stack contains the SEC base "mov [edx+secs.base], eax; \n" // secs->base = SEC base passed through eenter "eresume_flow:\n" "mov ebx, edx;\n" "add ebx, secs.vid;\n" "mov ax, 0;\n" //expect view 0 "mov cx, 1;\n" //switch vid to 1 ".byte 0xF0;\n" "cmpxchg [ebx], cx;\n" */ //****************debug only end*************** //Perform vmfunc to transition to required EPT trusted view //KCZ ecx has the view number already "mov rax, vmfunc_view_sw_ctrl;\n" #if 1 ".byte 0x0f;\n" ".byte 0x01;\n" ".byte 0xd4;\n" #else "vmfunc;\n" #endif "cmp rax, vmfunc_return_success;\n" "jne cancel_enter_eresume;\n" //"vmcall;\n" //VMCALL only for debug //VMFUNC opcode emitted below // ".byte 0x0f;\n" // ".byte 0x01;\n" // ".byte 0xd4;\n" //KCZ views are switched; should load cr3 but not doing that for now... //KCZ but before the switch, we have to store OS-cr3 for now before //KCZ we can save it in SECS //KCZ esi should be available "mov r12, cr3;\n" //r12 == os cr3. This will be saved in TCS later on. "mov rax, [rdx+secs.os_cr3];\n" "cmp r12, rax;\n" "je check_scv_trusted_view;\n" "cancel_enter_eresume:\n" "mov rax, vmfunc_view_sw_ctrl;\n" "mov rcx, untrusted_view_id;\n" #if 1 ".byte 0x0f;\n" ".byte 0x01;\n" ".byte 0xd4;\n" #else "vmfunc;\n" //Switch back to view 0 #endif "cancel_enter_no_view_switch:" //Recreate the untrusted stack again as it was emptied //at the beginning with an intention to switch to the //trusted view "mov rax, [r15+if_noskip_ec+if.ss];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.esp];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.eflags];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.cs];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.eip];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r8];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r9];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r10];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r11];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r12];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r13];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r14];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r15];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.edi];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.esi];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.ebx];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.edx];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.ecx];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.eax];\n" "push rax;\n" "mov rax, [r15];\n" //rip in VIDT_STUB "push rax;\n" //enter_enclave.S expects %edi as -1 for normal //exit, any other value of %edi is treated as //ocall return "mov edi, -1;\n" "mov [rsp+start_gprs+gprs.edi], edi;\n" //esi expects the return status from ecall. //return nonzero value "mov esi, ecall_not_allowed;\n" "mov [rsp+start_gprs+gprs.esi], esi;\n" "jmp out_enter_eresume;\n" "check_scv_trusted_view:\n" //Read SECS security cookie from SECS GVA (RDI) //Compare against immediate value on XO trampoline page "mov rax, [rdx+secs.scv];\n" "enter_eresume_code_cmp_patch2:\n" "mov r9, patch_str_secs_scv;\n" "cmp rax, r9;\n" "jz view_x_secs_check_ok;\n" "mov ecx, secs_scv_chk_fail;\n" "jmp gp_vmcall;\n" "view_x_secs_check_ok:\n" //Read view-handle expected from SECS //Verify view handle requested post view switch //and branch to fail if mis-match //(Fail vmcalls and injects #GP after restoring OS-CR3 and RCX) //KCZ ecx still has view id from the original edx "mov rax, [rdx+secs.eid];\n" "cmp eax, ecx;\n" "jz view_x_check_ok;\n" //Handle cases where some other thread destroys //the view while this thread is trying to enter //the view. Rather than causing panic in the //VMM, we exit out to the guest with reason of //ecall not allowed "jmp cancel_enter_eresume;\n" "view_x_check_ok:\n" //Verify SECS state to ensure View initialized //KCZ secs->attributes->init flag? //KCZ bit 0 of secs->attributes has to be set "mov rax, [rdx+secs.attrib];\n" "bt rax, 0;\n" "jc secs_inited_check_ok;\n" "jmp cancel_enter_eresume;\n" "secs_inited_check_ok:\n" //Cache OS-CR3 in GPR //KCZ shouldn't this have been done above? //KCZ in any case, OS-cr3 is in esi //load ACR3 from SECS //FIXME KCZ again, shouldn't this be done above before //KCZ we access secs_la? #ifdef ACTIVATE_ACR3 "mov rax, [rdx+secs.acr3];\n" "mov cr3, rax;\n" #endif //STACKFIX Ravi //cpu tss points to our tss which refs our r0 stack //and we can now access our r0 stack, but we have to set it up and pivot first //###save untrusted r0 rsp from r14 into TCS -> done later when TCS ready //###copy bulk info from enter-page r15 to new rsp from secs pcd //###pivot stack to trusted r0 stack "xor rcx, rcx;\n" "mov rbx, rdx;\n" //secs ptr "add rbx, secs.pcd;\n" "mov ecx, r13d;\n" //core id cached in r13 "shl ecx, shift_size_secs_pcd;\n" "add rcx, rbx;\n" //ptr to core-specific pcd "mov rsp, [rcx+secs.pcd.r0sp];\n" "mov rax, [r15+if_noskip_ec+if.ss];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.esp];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.eflags];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.cs];\n" "push rax;\n" "mov rax, [r15+if_noskip_ec+if.eip];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r8];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r9];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r10];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r11];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r12];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r13];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r14];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.r15];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.edi];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.esi];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.ebx];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.edx];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.ecx];\n" "push rax;\n" "mov rax, [r15+start_gprs+gprs.eax];\n" "push rax;\n" "mov rax, [r15];\n" "push rax;\n" "xor rbx, rbx;\n" //following code *should* work unchanged since it uses rsp as before //Verify RBX is page-aligned (for TCS) //KCZ if we are supposed to save OS-cr3 in TCS //KCZ shouldn't this check be done earlier? //KCZ !(rbx & 4095) is OK //VT: TODO: change ebx->rbx for 64 bit user space "mov ebx, [rsp+start_gprs+gprs.ebx];\n" "mov eax, ebx;\n" "and eax, 4095;\n" "cmp eax, 0;\n" "jz tcs_page_aligned_check_ok;\n" "mov ecx, tcs_pg_align_chk_fail;\n" "jmp gp_vmcall;\n" "tcs_page_aligned_check_ok:\n" // save OS-CR3 until we can write to TCS // VT: OS-CR3 is saved in r12 // "push rsi;\n" //Make sure SSA contains at least one frame // cssa "mov eax, [ebx+tcs.cssa];\n" // if this is eresume, jmp to a different check "cmp r10d, eresume_vector;\n" "jz check_ssa_for_eresume;\n" // compare cssa to nssa "mov esi, [ebx+tcs.nssa];\n" "cmp eax, esi;\n" // cssa must be < nssa "jb ssa_frame_avail_check_ok;\n" "mov ecx, ssa_frame_avl_chk1_fail;\n" "jmp gp_vmcall;\n" "check_ssa_for_eresume:\n" // there must be at least one active frame "cmp eax, 0;\n" "jnz ssa_frame_avail_check_ok;\n" //#endif "mov ecx, ssa_frame_avl_chk2_fail;\n" "jmp gp_vmcall;\n" "ssa_frame_avail_check_ok:\n" // calculate ssa //uint64_t ssa_start = tcs->arch.ossa + secs->baseaddr //+ SSA_FRAME_SIZE(secs) * tcs->arch.cssa; // assume ssa frame size is 4096 // eax has cssa already "push rdx;\n" //since mul clobbers eax, edx "mov ecx, 4096;\n" "mul ecx;\n" //RAVI modified to use ecx - confirm ok // KCZ yes, it is OK; I forgot mul must use r/m32 "pop rdx;\n" //since mul clobbers eax, edx - restore secs // add ossa "add eax, [ebx+tcs.ossa];\n" // add base address from SECS "add eax, [rdx+secs.base];\n" // if this is eresume, subtract one frame "mov rcx, r10;\n" //get vector "cmp rcx, eresume_vector;\n" "jnz ssa_address_done;\n" // for now it is hardcoded to one page "sub eax, 4096;\n" "ssa_address_done:\n" // push on the stack, it will be needed later "push rax;\n" // the stack looks like this //+------------+ //| ssa | 0 //+------------+ //Perform segment table validation //Verify that CS, SS, DS, ES.base is 0 // KCZ GDT and LDT bases are cached in SECS // KCZ because desc table exiting is on "xor rax, rax;\n" "mov eax, r13d;\n" //core id "shl eax, shift_size_secs_pcd;\n" "add rax, rdx;\n" "add rax, secs.pcd;\n" "mov rsi, [rax+secs.pcd.gdtr];\n" "mov rdi, [rax+secs.pcd.ldtr];\n" // GP_IF (cs.base) "xor eax, eax;\n" "mov eax, [rsp+if_noskip_ec+if.cs+8];\n" //+8 as there is a temporary additional push on stacks (for ssa)... similar change for SS below CHECK_SEGMENT_BASE("cs") // GP_IF (aep > cs.limit) ; // now edx has lo; ecx has hi // limit is 16 lsbs from edx and bits 16-19 from ecx // this is cs so no expanding down "and edx, 0xFFFF;\n" //check granularity of segment "bt ecx, seg_granularity;\n" "jc seg_g_4k_inc;\n" //byte granularity "and ecx, 0xF0000;\n" "or ecx, edx;\n" "jmp perf_aep_check;\n" "seg_g_4k_inc:\n" "and ecx, 0xF0000;\n" "or ecx, edx;\n" "shl ecx, shift_size_4k;\n" "perf_aep_check:\n" "mov edx, [ebx+tcs.aep];\n" "cmp ecx, edx;\n" "ja aep_check_ok;\n" "mov ecx, aep_chk_fail;\n" "jmp gp_vmcall;\n" "aep_check_ok:\n" // if eenter // target = tcs->arch.oentry + secs->baseaddr; // if eresume // target = gpr->rip // GP_IF (target > cs.limit); "mov eax, r10d;\n" //vector is saved in r10 "cmp eax, eresume_vector;\n" "jz eresume_target;\n" // oentry "mov eax, [ebx+tcs.oentry];\n" // add base address from SECS "enter_eresume_code_secs_patch2:\n" "mov rdx, patch_str_secs_ptr;\n" "add eax, [rdx+secs.base];\n" "jmp target_check;\n" "eresume_target:\n" // ssa is on the stack "mov edx, [rsp];\n" //but gprs are at the end "add edx, 4096;\n" "sub edx, ssa_gpr_size;\n" "mov eax, [edx+ssa.ip];\n" //TODO: For 64-bit userspace it must be copied in rax "target_check:\n" "cmp ecx, eax;\n" "ja target_check_ok;\n" "mov ecx, target_chk_fail;\n" "jmp gp_vmcall;\n" "target_check_ok:\n" // KCZ temp fix // moved ds checks after ss checks // KCZ temp fix // the problem is that USABLE macro in PAS uses a null bit in AR // I don't know if such a bit exists here bt if base is not 0 // but segement is somehow marked unusable, we will inject #GP // if (USABLE(es)) GP_IF (es.base); // if (USABLE(ss)) GP_IF (ss.base || !ss.ar.db); "xor eax, eax;\n" "mov ax, es;\n" CHECK_SEGMENT_BASE("es") "xor eax, eax;\n" "mov eax, [rsp+if_noskip_ec+if.ss+8];\n" CHECK_SEGMENT_BASE("ss") // ecx has ss.hi, check db bit "bt ecx, 22;\n" "jc ss_db_check_ok;\n" "mov ecx, ss_db_chk_fail;\n" "jmp gp_vmcall;\n" "ss_db_check_ok:\n" // KCZ temp fix // GP_IF (ds.base) "xor eax, eax;\n" "mov ax, ds;\n" CHECK_SEGMENT_BASE("ds") "push rcx;\n" // this check is in the PAS; ucode tests this by testing ebx against ds.limit // seems easier to do it the PAS way "bt ecx, 10;\n" "jnc ds_expand_check_ok;\n" "mov ecx, ds_expand_chk_fail;\n" "jmp gp_vmcall;\n" "ds_expand_check_ok:\n" // KCZ temp fix // check SSA and FS/GS base alignment "mov eax, [ebx+tcs.ossa];\n" "and eax, 4095;\n" "cmp eax, 0;\n" "jz ossa_page_aligned_check_ok;\n" "mov ecx, 0xd;\n" "jmp gp_vmcall;\n" "ossa_page_aligned_check_ok:\n" // tcs.ofs_base "mov eax, [ebx+48];\n" "and eax, 4095;\n" "cmp eax, 0;\n" "jz ofsbase_page_aligned_check_ok;\n" "mov ecx, ofsbase_page_align_chk_fail;\n" "jmp gp_vmcall;\n" "ofsbase_page_aligned_check_ok:\n" // tcs.ogs_base "mov eax, [ebx+56];\n" "and eax, 4095;\n" "cmp eax, 0;\n" "jz ogsbase_page_aligned_check_ok;\n" "mov ecx, 0xf;\n" "jmp gp_vmcall;\n" "ogsbase_page_aligned_check_ok:\n" // check that proposed FS/GS segments fall within DS // edx has ds.lo; ecx has ds.hi "and edx, 0xFFFF;\n" //check granularity of segment "bt ecx, 23;\n" "jc ds_g_4k_inc;\n" //byte granularity "and ecx, 0xF0000;\n" "or ecx, edx;\n" "jmp perf_fsgs_check;\n" "ds_g_4k_inc:\n" "and ecx, 0xF0000;\n" "or ecx, edx;\n" "shl ecx, shift_size_4k;\n" "perf_fsgs_check:\n" // ecx has granularity adjusted ds.limit now "enter_eresume_code_secs_patch3:\n" "mov rdx, patch_str_secs_ptr;\n" "mov eax, [rdx+secs.base];\n" "add eax, [ebx+48];\n" "add eax, [ebx+64];\n" // eax has enclave_base + ofs_base + ofs_limit // first, check for overflow (wrap-around) // if overflow, ds.limit must be more than 4GB, which is not possible // otherwise eax must be less than or equal to ds.limit "jnc no_fs_wrap_around_check_ok;\n" "mov ecx, no_fs_wrap_around_chk_fail;\n" "jmp gp_vmcall;\n" "no_fs_wrap_around_check_ok:\n" "cmp ecx, eax;\n" "ja fs_within_ds_check_ok;\n" "mov ecx, fs_within_ds_chk_fail;\n" "jmp gp_vmcall;\n" "fs_within_ds_check_ok:\n" // now check gs "mov eax, [rdx+secs.base];\n" "add eax, [ebx+56];\n" "add eax, [ebx+68];\n" "jnc no_gs_wrap_around_check_ok;\n" "mov ecx, no_gs_wrap_around_chk_fail;\n" "jmp gp_vmcall;\n" "no_gs_wrap_around_check_ok:\n" "cmp ecx, eax;\n" "ja gs_within_ds_check_ok;\n" "mov ecx, gs_within_ds_chk_fail;\n" "jmp gp_vmcall;\n" "gs_within_ds_check_ok:\n" // check if TCS.flags.reserved is all 0s "mov rax, [ebx+tcs.flags];\n" // "and eax, 0xFFFFFFFE;\n" "and rax, ~0x1;\n" // "jz tcs_reserved_0_3_check_ok;\n" "jz tcs_reserved_check_ok;\n" "mov ecx, tcs_reserved_chk_fail;\n" "jmp gp_vmcall;\n" #if 0 "tcs_reserved_0_3_check_ok:\n" "mov eax, [ebx+tcs.flags+4];\n" "and eax, 0xFFFFFFFF;\n" "jz tcs_reserved_4_7_check_ok;\n" "mov ecx, 0x15;\n" "jmp gp_vmcall;\n" "tcs_reserved_4_7_check_ok:\n" #endif "tcs_reserved_check_ok:\n" // Transition to next state: INACTIVE -> ACTIVE // Make sure we started in the INACTIVE state and are the only thread using // the TCS. Otherwise, the instruction fails w/ #GP // TCS must not be active "mov eax, 0;\n" "mov ecx, 1;\n" //RAVI added // add lock ".byte 0xF0;\n" // set to active //cmpxchg [ebx], 1;\n" "cmpxchg [ebx], ecx;\n" //RAVI modified "je tcs_lock_acquired_ok;\n" "mov ecx, tcs_lock_acquire_fail;\n" "jmp gp_vmcall;\n" #if 0 //VMCALL here to assert "mov eax, vmcall_assert;\n" "vmcall;\n" #endif "tcs_lock_acquired_ok:\n" // KCZ temp fix "pop rcx;\n" // ds.hi // KCZ temp fix ////////// EENTER/ERESUME can't fail beyond this point ///////////////// // we can now write to TCS // eax is available // tcs->ssa_featsave_ppa = ssa; "pop rax;\n" // save ssa in tcs "mov [ebx+tcs.ssa], eax;\n" // take are care of OS-CR3 and AEP // tcs->arch.aep = aep; it's in ecx on the stack "mov eax, [rsp+start_gprs+gprs.ecx];\n" "mov [ebx+tcs.aep], eax;\n" // OS-CR3 // VT: No longer saving OS CR3 on stack "mov [ebx+tcs.os_cr3], r12;\n" //STACKFIX Ravi //save untrusted r0stack we entered with "mov [ebx+tcs.ur0sp], r14;\n" // ecx has ds.hi // but we need to reuse it so save on stack // (could have pushed earlier instead of making copies) "push rcx;\n" // KCZ this comment is wrong; FS/GS are saved in TCS //Do the Guest FS/GS Swap from TCS – Save OS FS/GS to SSA "xor rax, rax;\n" "mov ax, fs;\n" //VT: Kernel FS and user fs are same // save in tcs "mov [ebx+tcs.save_fs_selector], ax;\n" "mov edx, eax;\n" // since we have to do *8 later // instead of shifting right by 3 and mult by 8 // just do and 0xFFF8 //shr eax, 3 "and eax, 0xFFF8;\n" "and edx, 4;\n" //fs.TI "cmp edx, 0;\n" "jne fs_use_ldt;\n" "add rax, rsi;\n" // address of fs descriptor "push rax;\n" "mov ecx, [rax];\n" "mov eax, [rax+4];\n" "jmp fs_done;\n" "fs_use_ldt:\n" "add rax, rdi;\n" // address of fs descriptor "push rax;\n" "mov ecx, [rax];\n" "mov eax, [rax+4];\n" "fs_done:\n" // save in tcs "mov [ebx+tcs.save_fs_desc_low], ecx;\n" "mov [ebx+tcs.save_fs_desc_high], eax;\n" "xor rax, rax;\n" "mov ax, gs;\n" "mov [ebx+tcs.save_gs_selector], ax;\n" "mov edx, eax;\n" //shr eax, 3 "and eax, 0xFFF8;\n" "and edx, 4;\n" "cmp edx, 0;\n" "jne gs_use_ldt;\n" "add rax, rsi;\n" // address of gs descriptor "push rax;\n" "mov ecx, [rax];\n" "mov eax, [rax+4];\n" "jmp gs_done;\n" "gs_use_ldt:\n" "add rax, rdi;\n" // address of gs descriptor "push rax;\n" "mov ecx, [rax];\n" "mov eax, [rax+4];\n" "gs_done:\n" "mov [ebx+tcs.save_gs_desc_low], ecx;\n" "mov [ebx+tcs.save_gs_desc_high], eax;\n" // do the swap // stack looks like this now //+------------+ //| ds.hi | 16 //+------------+ //| &fs | 8 //+------------+ //| &gs | 0 //+------------+ // we start with ds descriptor "mov ecx, [rsp+16];\n" // type &= 0x3 // clear bits 10 and 11 in hi "and ecx, 0xFFFFF3FF;\n" // type |= 0x1 // set bit 8 // s = 1; // set bit 12 // p = 1; // set bit 15 // db = 1; // set bit 22 // g = 1; // set bit 23 "or ecx, 0xC09100;\n" // at this point FS/GS are the same // so copy back to ds.hi "mov [rsp+16], ecx;\n" // write limit_15_00 and base_15_00 "enter_eresume_code_secs_patch4:\n" "mov rdx, patch_str_secs_ptr;\n" "mov rax, [rdx+secs.base];\n" // ofs_base "add rax, [ebx+48];\n" // eax has the whole fs base; push for later "push rax;\n" // stack looks like this now //+------------+ //| ds.hi | 24 //+------------+ //| &fs | 16 //+------------+ //| &gs | 8 //+------------+ //| fs.b | 0 //+------------+ // now build fs.lo // base_15_0 goes into msbs "shl eax, 16;\n" // limit_15_0 goes into lsbs // ofs_limit "mov ax, [ebx+64];\n" // eax has fs.lo now so write back // to the fs descriptor address on the stack "mov rcx, [rsp+16];\n" "mov [rcx], eax;\n" // now gs.lo "mov rax, [rdx+secs.base];\n" // ogs_base "add rax, [ebx+56];\n" // eax has the whole gs base; push for later "push rax;\n" // stack looks like this now //+------------+ //| ds.hi | 32 //+------------+ //| &fs | 24 //+------------+ //| &gs | 16 //+------------+ //| fs.b | 8 //+------------+ //| gs.b | 0 //+------------+ "shl eax, 16;\n" // ogs_limit "mov ax, [ebx+68];\n" "mov rcx, [rsp+16];\n" "mov [rcx], eax;\n" // done with lo parts // read ds.hi "mov eax, [rsp+32];\n" // we only need the bits in the middle "and eax, 0x00FFFF00;\n" // read fs.b "mov ecx, [rsp+8];\n" // we only need 16 msbs "shr ecx, 16;\n" // copy 8 lsbs "or al, cl;\n" // now we need 8 msbs to be at the very top "shl ecx, 16;\n" //RAVI changed typo to shl // but don't need anything else "and ecx, 0xFF000000;\n" "or eax, ecx;\n" // we have the whole fs.hi now "mov rcx, [rsp+24];\n" // so write back to the correct place "mov [rcx+4], eax;\n" // same thing for gs // read ds.hi "mov eax, [rsp+32];\n" // we only need the bits in the middle "and eax, 0x00FFFF00;\n" // read gs.b "mov ecx, [rsp];\n" // we only need 16 msbs "shr ecx, 16;\n" // copy 8 lsbs "or al, cl;\n" // now we need 8 msbs to be at the very top "shl ecx, 16;\n" //RAVI modified typo srl to shl - confirm // KCZ yes, shl is correct // but don't need anything else "and ecx, 0xFF000000;\n" "or eax, ecx;\n" // we have the whole fs.hi now "mov rcx, [rsp+16];\n" // so write back to the correct place "mov [rcx+4], eax;\n" // finished swapping FS/GS // pop the stack "add rsp, 40;\n" // shouldn't we force reload of FS/GS here? //RAVI - yes we should "mov fs, [ebx+tcs.save_fs_selector];\n" "mov gs, [ebx+tcs.save_gs_selector];\n" // if fs selector is 0 use gs selector // NOTE this only works if proposed fsbase == gsbase // and proposed fslim == gslim "xor rax, rax;\n" "mov ax, fs;\n" "cmp ax, 0;\n" "jne continue_enter_eresume;\n" "mov fs, [ebx+tcs.save_gs_selector];\n" "continue_enter_eresume:\n" // get stored ssa from tcs "mov eax, [ebx+tcs.ssa];\n" //gpr area is at the end of ssa page // gpr = ssa + SE_PAGE_SIZE - sizeof(gpr_t); "add eax, 4096;\n" "sub eax, ssa_gpr_size;\n" // rflags from the stack "mov rdx, [rsp+if_noskip_ec+if.eflags];\n" // if this is eresume, restore values stored by AEX "mov ecx, r10d;\n" "cmp ecx, eresume_vector;\n" "jz eresume_restore_state;\n" //Save state for possible asynchronous exits //Save the outside RSP and RBP so they can be restored //on next asynch or synch exit //Setup stack to be switched to trusted view stack by //setting RSP on interrupt frame // save u_rsp and u_rbp "mov rcx, [rsp+if_noskip_ec+if.esp];\n" "mov [eax+ssa.sp_u], rcx;\n" "mov [eax+ssa.bp_u], rbp;\n" //VT: Since we are not writing // ISRs in C, the rbp is still the user rbp. A 'C' code // requires rbp to be saved on the stack and the new rbp // be created from kernel rsp // save EFLAGS.TF - needed only for OPTOUT enclave // tcs->rflags = rflags.raw; // VT: low 32 bits are good enough since bits 22:63 // for rflags are reserved. "mov [ebx+tcs.eflags], edx;\n" // clear TF "and edx, 0xFFFFFEFF;\n" #if 0 //clear IF - for debuggin purpose only "and edx, 0xFFFFFDFF;\n" #endif "jmp prepare_for_enter;\n" "eresume_restore_state:\n" "mov eax, [ebx+tcs.ssa];\n" "fxrstor64 [eax];\n" "add eax, 4096;\n" "sub eax, ssa_gpr_size;\n" // rax "mov rcx, [eax+ssa.ax];\n" "mov [rsp+start_gprs+gprs.eax], rcx;\n" // rcx "mov rcx, [eax+ssa.cx];\n" "mov [rsp+start_gprs+gprs.ecx], rcx;\n" // rdx "mov rcx, [eax+ssa.dx];\n" "mov [rsp+start_gprs+gprs.edx], rcx;\n" // rbx "mov rcx, [eax+ssa.bx];\n" "mov [rsp+start_gprs+gprs.ebx], rcx;\n" // rsi "mov rcx, [eax+ssa.si];\n" "mov [rsp+start_gprs+gprs.esi], rcx;\n" // rdi "mov rcx, [eax+ssa.di];\n" "mov [rsp+start_gprs+gprs.edi], rcx;\n" // r8 "mov rcx, [eax+ssa.r8];\n" "mov [rsp+start_gprs+gprs.r8], rcx;\n" // r9 "mov rcx, [eax+ssa.r9];\n" "mov [rsp+start_gprs+gprs.r9], rcx;\n" // r10 "mov rcx, [eax+ssa.r10];\n" "mov [rsp+start_gprs+gprs.r10], rcx;\n" // r11 "mov rcx, [eax+ssa.r11];\n" "mov [rsp+start_gprs+gprs.r11], rcx;\n" // r12 "mov rcx, [eax+ssa.r12];\n" "mov [rsp+start_gprs+gprs.r12], rcx;\n" // r13 "mov rcx, [eax+ssa.r13];\n" "mov [rsp+start_gprs+gprs.r13], rcx;\n" // r14 "mov rcx, [eax+ssa.r14];\n" "mov [rsp+start_gprs+gprs.r14], rcx;\n" // r15 "mov rcx, [eax+ssa.r15];\n" "mov [rsp+start_gprs+gprs.r15], rcx;\n" // rbp "mov rcx, [eax+ssa.bp];\n" "mov rbp, rcx;\n" // rsp "mov rcx, [eax+ssa.sp];\n" "mov [rsp+if_noskip_ec+if.esp], rcx;\n" // rflags [start] "mov rdx, [eax+ssa.flags];\n" "mov ecx, [rsp+if_noskip_ec+if.eflags];\n" // restore tf "and ecx, 0x100;\n" "or edx, ecx;\n" "mov [rsp+if_noskip_ec+if.eflags], rdx;\n" // eflags [end] //if resume flow should resume to ssa.ip "mov rsi, [eax+ssa.ip];\n" // this is still eresume flow // must decrement cssa "mov eax, [ebx+24];\n" "dec eax;\n" "mov [ebx+24], eax;\n" "jmp eresume_restore_eip;\n" "prepare_for_enter:\n" // only eenter flow goes here. // eflags back on the stack //************debug only start************ //"and edx, 0xFFFFFDFF;\n" //set IF=0 for test //************debug only end************** "mov [rsp+if_noskip_ec+if.eflags], rdx;\n" //if enter then //Setup entrypoint RIP to TCS.OENTRY entrypoint by setting //RIP on interrupt frame // int saves correct address "mov rax, [rsp+if_noskip_ec+if.eip];\n" // ecx "mov [rsp+start_gprs+gprs.ecx], rax;\n" // cssa "mov eax, [ebx+tcs.cssa];\n" // eax "mov [rsp+start_gprs+gprs.eax], eax;\n" //IRET to continue execution at entrypoint point inside TV //if enter: entry point secs->base + tcs->oentry "enter_eresume_code_secs_patch5:\n" "mov rdx, patch_str_secs_ptr;\n" "mov rax, [rdx+secs.base];\n" "add rax, [ebx+tcs.oentry];\n" //Entry point of trusted is changed on stack "mov [rsp+if_noskip_ec+if.eip], rax;\n" "jmp enter_resume_last_step;\n" "eresume_restore_eip:\n" //if resume: entry point is from ssa.ip //which is stored in esi "mov [rsp+if_noskip_ec+if.eip], esi;\n" "enter_resume_last_step:\n" // last step: write tcs address to secs[core#] "xor rax, rax;\n" "mov eax, r13d;\n" //cpu id // size of per-core-data is shift_size_secs_pcd "shl eax, shift_size_secs_pcd;\n" "add eax, secs.pcd;\n" "add eax, secs.pcd.tcs;\n" "enter_eresume_code_secs_patch6:\n" "mov rdx, patch_str_secs_ptr;\n" // Putting edx closer to its consumption "mov [rdx+rax], ebx;\n" "out_enter_eresume:\n" "mov rax, 0;\n" //return flag to indicate use IRET not RET "ret;\n" //We return a flag in eax to MyHandler so that it checks that //and either does a RET to the OS handler (like AEX) to address in edi OR //IRETs for our INT flows, in either case, MyHandler leaves only the //required intr frame on the appropriate stack before issueing IRET or RET //IRET cleans up the intr frame, and RET does not (as intended) "gp_vmcall:\n" "mov eax, vmcall_assert;\n" "vmcall;\n" ".att_syntax\n" ); void enter_eresume_code_end(void); __asm__ ( ".globl enter_eresume_code_end\n" "enter_eresume_code_end:" "nop;\n" );