diff --git a/Documentation/Trusty.txt b/Documentation/Trusty.txt index 141acc148..38b8a85e5 100644 --- a/Documentation/Trusty.txt +++ b/Documentation/Trusty.txt @@ -49,8 +49,8 @@ Note: Trusty OS is running in Secure World in the architecture above. ************************** Trusty specific Hypercalls ************************** -1. HC_LAUNCH_TRUSTY - ->This Hypercall is used by UOSloader (User OS Bootloader) to request ACRN to launch Trusty. +1. HC_INITIALIZE_TRUSTY + ->This Hypercall is used by UOS_Loader to request ACRN to initialize Trusty. ->The Trusty memory region range, entry point must be specified. ->Hypervisor needs to save current vCPU contexts (Normal World). 2. HC_WORLD_SWITCH @@ -60,7 +60,7 @@ Trusty specific Hypercalls API --- -1. hcall_launch_trusty(vm_t *vm); +1. hcall_initialize_trusty(vm_t *vm); 2. hcall_world_switch(vm_t *vm); @@ -71,12 +71,12 @@ Per design, UOSloader will trigger boot of Trusty. So the boot flow will be: UOSloader --> ACRN --> Trusty --> ACRN --> UOSloader Detail: -1. UOSloader +1. UOS_Loader 1.1 load and verify trusty image from virtual disk. 1.2 allocate runtime memory for trusty. 1.3 do ELF relocation of trusty image and get entry address. - 1.4 call HC_LAUNCH_TRUSTY with trusty memory base and entry address. -2. ACRN(HC_LAUNCH_TRUSTY) + 1.4 call HC_INITIALIZE_TRUSTY with trusty memory base and entry address. +2. ACRN(HC_INITIALIZE_TRUSTY) 2.1 save World context for Normal World. 2.2 init World context for Secure World(RIP, RSP, EPT, etc.). 2.3 resume to Secure World. @@ -85,9 +85,9 @@ Detail: 3.2 call HC_WORLD_SWITCH to switch back to Normal World if boot completed. 4. ACRN(HC_WORLD_SWITCH) 4.1 save World context for the World which caused this vmexit(Secure World) - 4.2 restore World context for next World(Normal World(UOSloader)) - 4.3 resume to next World(UOSloader) -5. UOSloader + 4.2 restore World context for next World(Normal World(UOS_Loader)) + 4.3 resume to next World(UOS_Loader) +5. UOS_Loader 5.1 continue to boot. diff --git a/arch/x86/guest/vmcall.c b/arch/x86/guest/vmcall.c index c78344ebc..72b6d0b74 100644 --- a/arch/x86/guest/vmcall.c +++ b/arch/x86/guest/vmcall.c @@ -138,6 +138,10 @@ int vmcall_handler(struct vcpu *vcpu) ret = hcall_world_switch(vcpu); break; + case HC_INITIALIZE_TRUSTY: + ret = hcall_initialize_trusty(vcpu, param1); + break; + default: pr_err("op %d: Invalid hypercall\n", hypcall_id); ret = -1; diff --git a/arch/x86/trusty.c b/arch/x86/trusty.c index 0a9bfe4eb..2eb91f1d2 100644 --- a/arch/x86/trusty.c +++ b/arch/x86/trusty.c @@ -39,6 +39,38 @@ _Static_assert(NR_WORLD == 2, "Only 2 Worlds supported!"); /* Trusty EPT rebase gpa: 511G */ #define TRUSTY_EPT_REBASE_GPA (511ULL*1024ULL*1024ULL*1024ULL) +#define TRUSTY_VERSION 1 + +struct trusty_startup_param { + uint32_t size_of_this_struct; + uint32_t mem_size; + uint64_t tsc_per_ms; + uint64_t trusty_mem_base; + uint32_t reserved; + uint8_t padding[4]; +}; + +struct trusty_mem { + /* The first page of trusty memory is reserved for key_info and + * trusty_startup_param. + */ + union { + struct { + struct key_info key_info; + struct trusty_startup_param startup_param; + }; + uint8_t page[CPU_PAGE_SIZE]; + } first_page; + + /* The left memory is for trusty's code/data/heap/stack + */ + uint8_t left_mem[0]; +}; + +_Static_assert(sizeof(struct trusty_startup_param) + + sizeof(struct key_info) < 0x1000, + "trusty_startup_param + key_info > 1Page size(4KB)!"); + #define save_segment(seg, SEG_NAME) \ { \ seg.selector = exec_vmread(VMX_GUEST_##SEG_NAME##_SEL); \ @@ -239,3 +271,104 @@ void switch_world(struct vcpu *vcpu, int next_world) /* Update world index */ arch_vcpu->cur_context = next_world; } + +/* Put key_info and trusty_startup_param in the first Page of Trusty + * runtime memory + */ +static void setup_trusty_info(struct vcpu *vcpu, + uint32_t mem_size, uint64_t mem_base_hpa) +{ + struct trusty_mem *mem; + + mem = (struct trusty_mem *)(HPA2HVA(mem_base_hpa)); + + /* TODO: prepare vkey_info */ + + /* Prepare trusty startup info */ + mem->first_page.startup_param.size_of_this_struct = + sizeof(struct trusty_startup_param); + mem->first_page.startup_param.mem_size = mem_size; + mem->first_page.startup_param.tsc_per_ms = TIME_MS_DELTA; + mem->first_page.startup_param.trusty_mem_base = TRUSTY_EPT_REBASE_GPA; + + /* According to trusty boot protocol, it will use RDI as the + * address(GPA) of startup_param on boot. Currently, the startup_param + * is put in the first page of trusty memory just followed by key_info. + */ + vcpu->arch_vcpu.contexts[SECURE_WORLD].guest_cpu_regs.regs.rdi + = (uint64_t)TRUSTY_EPT_REBASE_GPA + sizeof(struct key_info); +} + +/* Secure World will reuse environment of UOS_Loder since they are + * both booting from and running in 64bit mode, except GP registers. + * RIP, RSP and RDI are specified below, other GP registers are leaved + * as 0. + */ +static void init_secure_world_env(struct vcpu *vcpu, + uint64_t entry_gpa, + uint64_t base_hpa, + uint32_t size) +{ + vcpu->arch_vcpu.inst_len = 0; + vcpu->arch_vcpu.contexts[SECURE_WORLD].rip = entry_gpa; + vcpu->arch_vcpu.contexts[SECURE_WORLD].rsp = + TRUSTY_EPT_REBASE_GPA + size; + exec_vmwrite(VMX_GUEST_RSP, + TRUSTY_EPT_REBASE_GPA + size); + + setup_trusty_info(vcpu, size, base_hpa); +} + +bool initialize_trusty(struct vcpu *vcpu, uint64_t param) +{ + uint64_t trusty_entry_gpa, trusty_base_gpa, trusty_base_hpa; + struct vm *vm = vcpu->vm; + struct trusty_boot_param *boot_param = + (struct trusty_boot_param *)(gpa2hpa(vm, param)); + + if (sizeof(struct trusty_boot_param) != + boot_param->size_of_this_struct) { + pr_err("%s: sizeof(struct trusty_boot_param) mismatch!\n", + __func__); + return false; + } + + if (boot_param->version != TRUSTY_VERSION) { + pr_err("%s: version of(trusty_boot_param) mismatch!\n", + __func__); + return false; + } + + if (!boot_param->entry_point) { + pr_err("%s: Invalid entry point\n", __func__); + return false; + } + + if (!boot_param->base_addr) { + pr_err("%s: Invalid memory base address\n", __func__); + return false; + } + + trusty_entry_gpa = (uint64_t)boot_param->entry_point; + trusty_base_gpa = (uint64_t)boot_param->base_addr; + + create_secure_world_ept(vm, trusty_base_gpa, boot_param->mem_size, + TRUSTY_EPT_REBASE_GPA); + trusty_base_hpa = vm->sworld_control.sworld_memory.base_hpa; + + exec_vmwrite64(VMX_EPT_POINTER_FULL, + ((uint64_t)vm->arch_vm.sworld_eptp) | (3<<3) | 6); + + /* save Normal World context */ + save_world_ctx(&vcpu->arch_vcpu.contexts[NORMAL_WORLD]); + + /* init secure world environment */ + init_secure_world_env(vcpu, + trusty_entry_gpa - trusty_base_gpa + TRUSTY_EPT_REBASE_GPA, + trusty_base_hpa, boot_param->mem_size); + + /* switch to Secure World */ + vcpu->arch_vcpu.cur_context = SECURE_WORLD; + + return true; +} diff --git a/common/trusty_hypercall.c b/common/trusty_hypercall.c index 14a88369b..0789ef990 100644 --- a/common/trusty_hypercall.c +++ b/common/trusty_hypercall.c @@ -51,7 +51,7 @@ int64_t hcall_world_switch(struct vcpu *vcpu) } if (!vcpu->vm->arch_vm.sworld_eptp) { - pr_err("Trusty is not launched!\n"); + pr_err("Trusty is not initialized!\n"); return -1; } @@ -61,3 +61,31 @@ int64_t hcall_world_switch(struct vcpu *vcpu) switch_world(vcpu, next_world_id); return 0; } + +int64_t hcall_initialize_trusty(struct vcpu *vcpu, uint64_t param) +{ + if (!is_hypercall_from_ring0()) { + pr_err("%s() is only allowed from RING-0!\n", __func__); + return -1; + } + + if (!vcpu->vm->sworld_control.sworld_enabled) { + pr_err("Secure World is not enabled!\n"); + return -1; + } + + if (vcpu->vm->arch_vm.sworld_eptp) { + pr_err("Trusty already initialized!\n"); + return -1; + } + + ASSERT(vcpu->arch_vcpu.cur_context == NORMAL_WORLD, + "The Trusty Initialize hypercall must be from Normal World"); + + if (!initialize_trusty(vcpu, param)) { + pr_err("Failed to initialize trusty!\n"); + return -1; + } + + return 0; +} diff --git a/include/arch/x86/trusty.h b/include/arch/x86/trusty.h index 966c6156a..f7ef077b8 100644 --- a/include/arch/x86/trusty.h +++ b/include/arch/x86/trusty.h @@ -129,6 +129,7 @@ struct secure_world_control { }; void switch_world(struct vcpu *vcpu, int next_world); +bool initialize_trusty(struct vcpu *vcpu, uint64_t param); #endif /* TRUSTY_H_ */ diff --git a/include/common/hypercall.h b/include/common/hypercall.h index b89951ce1..ec9a49faa 100644 --- a/include/common/hypercall.h +++ b/include/common/hypercall.h @@ -335,6 +335,17 @@ int64_t hcall_setup_sbuf(struct vm *vm, uint64_t param); */ int64_t hcall_world_switch(struct vcpu *vcpu); +/** + * @brief Initialize environment for Trusty-OS on a VCPU. + * + * @param VCPU Pointer to VCPU data structure + * @param param's guest physical address. This gpa points to + * struct trusty_boot_param + * + * @return 0 on success, non-zero on error. + */ +int64_t hcall_initialize_trusty(struct vcpu *vcpu, uint64_t param); + /** * @} */ diff --git a/include/public/acrn_hv_defs.h b/include/public/acrn_hv_defs.h index 15964835f..8ce4c4015 100644 --- a/include/public/acrn_hv_defs.h +++ b/include/public/acrn_hv_defs.h @@ -95,7 +95,7 @@ /* Trusty */ #define HC_ID_TRUSTY_BASE 0x70UL -#define HC_LAUNCH_TRUSTY _HC_ID(HC_ID, HC_ID_TRUSTY_BASE + 0x00) +#define HC_INITIALIZE_TRUSTY _HC_ID(HC_ID, HC_ID_TRUSTY_BASE + 0x00) #define HC_WORLD_SWITCH _HC_ID(HC_ID, HC_ID_TRUSTY_BASE + 0x01) #define HC_GET_SEC_INFO _HC_ID(HC_ID, HC_ID_TRUSTY_BASE + 0x02) @@ -226,6 +226,26 @@ struct hc_api_version { uint32_t minor_version; } __aligned(8); +/** + * Trusty boot params, used for HC_INITIALIZE_TRUSTY + */ +struct trusty_boot_param { + /** sizeof this structure */ + uint32_t size_of_this_struct; + + /** version of this structure */ + uint32_t version; + + /** trusty runtime memory base address */ + uint32_t base_addr; + + /** trusty entry point */ + uint32_t entry_point; + + /** trusty runtime memory size */ + uint32_t mem_size; +} __aligned(8); + /** * @} */