trusty: implement hypercall to initialize trusty

UOS_Loader will trigger boot of Trusty-OS by HC_INITIALIZE_TRUSTY.
UOS_Loader will load trusty image and alloc runtime memory for
trusty. UOS_Loader will transfer these information include
trusty runtime memory base address, entry address and memory
size to hypervisor by trusty_boot_param structure.

In hypervisor, once HC_INITIALIZE_TRUSTY received, it will create
EPT for Secure World, save Normal World vCPU context, init
Secure World vCPU context and switch World state to Secure World.

Signed-off-by: Qi Yadong <yadong.qi@intel.com>
This commit is contained in:
Qi Yadong 2018-03-27 17:27:51 +08:00 committed by lijinxia
parent 74929d7fe5
commit c4254179bc
7 changed files with 208 additions and 11 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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);
/**
* @}
*/

View File

@ -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);
/**
* @}
*/