hv: tee: add x86_tee hypercall interfaces

This patch adds the x86_tee hypercall interfaces.

- HC_TEE_VCPU_BOOT_DONE

This hypercall is used to notify the hypervisor that the TEE VCPU Boot
is done, so that we can sleep the corresponding TEE VCPU. REE will be
started at the last time this hypercall is called by TEE.

- HC_SWITCH_EE

For REE VM, it uses this hypercall to request TEE service.

For TEE VM, it uses this hypercall to switch back to REE
when it completes the REE service.

Tracked-On: #6571
Signed-off-by: Jie Deng <jie.deng@intel.com>
Reviewed-by: Wang, Yu1 <yu1.wang@intel.com>
Acked-by: Eddie Dong <eddie.dong@Intel.com>
This commit is contained in:
Jie Deng 2021-11-12 15:13:08 +08:00 committed by wenlingz
parent f3792a74a3
commit 3c9c41b656
6 changed files with 92 additions and 16 deletions

View File

@ -11,6 +11,7 @@
#include <asm/guest/optee.h>
#include <asm/trampoline.h>
#include <reloc.h>
#include <hypercall.h>
void prepare_tee_vm_memmap(struct acrn_vm *vm, const struct acrn_vm_config *vm_config)
{
@ -33,3 +34,15 @@ void prepare_tee_vm_memmap(struct acrn_vm *vm, const struct acrn_vm_config *vm_c
ept_del_mr(vm, (uint64_t *)vm->arch_vm.nworld_eptp, hv_hpa, get_hv_ram_size());
}
}
int32_t hcall_handle_tee_vcpu_boot_done(struct acrn_vcpu *vcpu, __unused struct acrn_vm *target_vm,
__unused uint64_t param1, __unused uint64_t param2)
{
return 0;
}
int32_t hcall_switch_ee(struct acrn_vcpu *vcpu, __unused struct acrn_vm *target_vm,
__unused uint64_t param1, __unused uint64_t param2)
{
return 0;
}

View File

@ -978,7 +978,7 @@ static uint8_t loaded_pre_vm_nr = 0U;
*
* @pre vm_id < CONFIG_MAX_VM_NUM && vm_config != NULL
*/
void prepare_vm(uint16_t vm_id, struct acrn_vm_config *vm_config)
int32_t prepare_vm(uint16_t vm_id, struct acrn_vm_config *vm_config)
{
int32_t err = 0;
struct acrn_vm *vm = NULL;
@ -1024,17 +1024,9 @@ void prepare_vm(uint16_t vm_id, struct acrn_vm_config *vm_config)
if (is_prelaunched_vm(vm)) {
loaded_pre_vm_nr++;
}
if (err == 0) {
/* start vm BSP automatically */
start_vm(vm);
pr_acrnlog("Start VM id: %x name: %s", vm_id, vm_config->name);
} else {
pr_err("Failed to load VM id: %x name: %s, error = %d", vm_id, vm_config->name, err);
}
}
return err;
}
/**
@ -1048,12 +1040,35 @@ void launch_vms(uint16_t pcpu_id)
for (vm_id = 0U; vm_id < CONFIG_MAX_VM_NUM; vm_id++) {
vm_config = get_vm_config(vm_id);
if ((vm_config->guest_flags & GUEST_FLAG_REE) != 0U &&
(vm_config->guest_flags & GUEST_FLAG_TEE) != 0U) {
ASSERT(false, "%s: Wrong VM (VM id: %u) configuration, can't set both REE and TEE flags",
__func__, vm_id);
}
if ((vm_config->load_order == SERVICE_VM) || (vm_config->load_order == PRE_LAUNCHED_VM)) {
if (pcpu_id == get_configured_bsp_pcpu_id(vm_config)) {
if (vm_config->load_order == SERVICE_VM) {
service_vm_ptr = &vm_array[vm_id];
}
prepare_vm(vm_id, vm_config);
/*
* We can only start a VM when there is no error in prepare_vm.
* Otherwise, print out the corresponding error.
*
* We can only start REE VM when get the notification from TEE VM.
* so skip "start_vm" here for REE, and start it in TEE hypercall
* HC_TEE_VCPU_BOOT_DONE.
*/
if (prepare_vm(vm_id, vm_config) == 0) {
if ((vm_config->guest_flags & GUEST_FLAG_REE) != 0U) {
/* Nothing need to do here, REE will start in TEE hypercall */
} else {
start_vm(get_vm_from_vmid(vm_id));
pr_acrnlog("Start VM id: %x name: %s", vm_id, vm_config->name);
}
}
}
}
}

View File

@ -104,6 +104,12 @@ static const struct hc_dispatch hc_dispatch_table[] = {
[HC_IDX(HC_SAVE_RESTORE_SWORLD_CTX)] = {
.handler = hcall_save_restore_sworld_ctx,
.permission_flags = GUEST_FLAG_SECURE_WORLD_ENABLED},
[HC_IDX(HC_TEE_VCPU_BOOT_DONE)] = {
.handler = hcall_handle_tee_vcpu_boot_done,
.permission_flags = GUEST_FLAG_TEE},
[HC_IDX(HC_SWITCH_EE)] = {
.handler = hcall_switch_ee,
.permission_flags = (GUEST_FLAG_TEE | GUEST_FLAG_REE)},
};
uint16_t allocate_dynamical_vmid(struct acrn_vm_creation *cv)
@ -123,6 +129,18 @@ uint16_t allocate_dynamical_vmid(struct acrn_vm_creation *cv)
}
#define GUEST_FLAGS_ALLOWING_HYPERCALLS GUEST_FLAG_SECURE_WORLD_ENABLED
static bool is_guest_hypercall(struct acrn_vm *vm)
{
uint64_t guest_flags = get_vm_config(vm->vm_id)->guest_flags;
bool ret = true;
if ((guest_flags & (GUEST_FLAG_SECURE_WORLD_ENABLED |
GUEST_FLAG_TEE | GUEST_FLAG_REE)) == 0UL) {
ret = false;
}
return ret;
}
struct acrn_vm *parse_target_vm(struct acrn_vm *service_vm, uint64_t hcall_id, uint64_t param1, __unused uint64_t param2)
{
@ -214,7 +232,7 @@ static int32_t dispatch_hypercall(struct acrn_vcpu *vcpu)
put_vm_lock(target_vm);
}
} else if ((permission_flags != 0UL) &&
((guest_flags & permission_flags) == permission_flags)) {
((guest_flags & permission_flags) != 0UL)) {
ret = dispatch->handler(vcpu, vcpu->vm, param1, param2);
} else {
/* The vCPU is not allowed to invoke the given hypercall. Keep `ret` as -EINVAL and no
@ -238,7 +256,6 @@ int32_t vmcall_vmexit_handler(struct acrn_vcpu *vcpu)
struct acrn_vm *vm = vcpu->vm;
/* hypercall ID from guest*/
uint64_t hypcall_id = vcpu_get_gpreg(vcpu, CPU_REG_R8);
uint64_t guest_flags = get_vm_config(vm->vm_id)->guest_flags;
/*
* The following permission checks are applied to hypercalls.
@ -251,7 +268,7 @@ int32_t vmcall_vmexit_handler(struct acrn_vcpu *vcpu)
* guest flags. Attempts to invoke an unpermitted hypercall will make a vCPU see -EINVAL as the return
* value. No exception is triggered in this case.
*/
if (!is_service_vm(vm) && ((guest_flags & GUEST_FLAGS_ALLOWING_HYPERCALLS) == 0UL)) {
if (!is_service_vm(vm) && !is_guest_hypercall(vm)) {
vcpu_inject_ud(vcpu);
ret = -ENODEV;
} else if (!is_hypercall_from_ring0()) {

View File

@ -233,7 +233,7 @@ void resume_vm_from_s3(struct acrn_vm *vm, uint32_t wakeup_vec);
void start_vm(struct acrn_vm *vm);
int32_t reset_vm(struct acrn_vm *vm);
int32_t create_vm(uint16_t vm_id, uint64_t pcpu_bitmap, struct acrn_vm_config *vm_config, struct acrn_vm **rtn_vm);
void prepare_vm(uint16_t vm_id, struct acrn_vm_config *vm_config);
int32_t prepare_vm(uint16_t vm_id, struct acrn_vm_config *vm_config);
void launch_vms(uint16_t pcpu_id);
bool is_poweroff_vm(const struct acrn_vm *vm);
bool is_created_vm(const struct acrn_vm *vm);

View File

@ -477,6 +477,32 @@ int32_t hcall_initialize_trusty(struct acrn_vcpu *vcpu, struct acrn_vm *target_v
int32_t hcall_save_restore_sworld_ctx(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm,
uint64_t param1, uint64_t param2);
/**
* @brief Handle the TEE boot done signal.
*
* @param vcpu Pointer to VCPU data structure
* @param target_vm not used
* @param param1 not used
* @param param2 not used
*
* @return 0 on success, non-zero on error.
*/
int32_t hcall_handle_tee_vcpu_boot_done(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm,
uint64_t param1, uint64_t param2);
/**
* @brief Switch the execution environment.
*
* @param vcpu Pointer to VCPU data structure
* @param target_vm not used
* @param param1 not used
* @param param2 not used
*
* @return 0 on success, non-zero on error.
*/
int32_t hcall_switch_ee(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm,
uint64_t param1, uint64_t param2);
/**
* @}
*/

View File

@ -89,6 +89,11 @@
#define HC_ID_PM_BASE 0x80UL
#define HC_PM_GET_CPU_STATE BASE_HC_ID(HC_ID, HC_ID_PM_BASE + 0x00UL)
/* X86 TEE */
#define HC_ID_TEE_BASE 0x90UL
#define HC_TEE_VCPU_BOOT_DONE BASE_HC_ID(HC_ID, HC_ID_TEE_BASE + 0x00UL)
#define HC_SWITCH_EE BASE_HC_ID(HC_ID, HC_ID_TEE_BASE + 0x01UL)
#define ACRN_INVALID_VMID (0xffffU)
#define ACRN_INVALID_HPA (~0UL)