android_kernel_modules_leno.../sldriver/isr64.c

2668 lines
83 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <linux/spinlock.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/smp.h>
#include <linux/slab.h>
#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 <linux/mm.h>
#include <linux/sched.h>
#endif
//#include <linux/notifier.h>
#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 <rw>
"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 <ro>
//### 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 <rw>
"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 <rw>
"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 <ro>
//### 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<rw>
"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<ro> 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"
);