mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-04-28 19:54:01 +00:00
The current permission-checking and dispatching mechanism of hypercalls is not unified because: 1. Some hypercalls require the exact vCPU initiating the call, while the others only need to know the VM. 2. Different hypercalls have different permission requirements: the trusty-related ones are enabled by a guest flag, while the others require the initiating VM to be the Service OS. Without a unified logic it could be hard to scale when more kinds of hypercalls are added later. The objectives of this patch are as follows. 1. All hypercalls have the same prototype and are dispatched by a unified logic. 2. Permissions are checked by a unified logic without consulting the hypercall ID. To achieve the first objective, this patch modifies the type of the first parameter of hcall_* functions (which are the callbacks implementing the hypercalls) from `struct acrn_vm *` to `struct acrn_vcpu *`. The doxygen-style documentations are updated accordingly. To achieve the second objective, this patch adds to `struct hc_dispatch` a `permission_flags` field which specifies the guest flags that must ALL be set for a VM to be able to invoke the hypercall. The default value (which is 0UL) indicates that this hypercall is for SOS only. Currently only the `permission_flag` of trusty-related hypercalls have the non-zero value GUEST_FLAG_SECURE_WORLD_ENABLED. With `permission_flag`, the permission checking logic of hypercalls is unified as follows. 1. General checks i. If the VM is neither SOS nor having any guest flag that allows certain hypercalls, it gets #UD upon executing the `vmcall` instruction. ii. If the VM is allowed to execute the `vmcall` instruction, but attempts to execute it in ring 1, 2 or 3, the VM gets #GP(0). 2. Hypercall-specific checks i. If the hypercall is for SOS (i.e. `permission_flag` is 0), the initiating VM must be SOS and the specified target VM cannot be a pre-launched VM. Otherwise the hypercall returns -EINVAL without further actions. ii. If the hypercall requires certain guest flags, the initiating VM must have all the required flags. Otherwise the hypercall returns -EINVAL without further actions. iii. A hypercall with an unknown hypercall ID makes the hypercall returns -EINVAL without further actions. The logic above is different from the current implementation in the following aspects. 1. A pre-launched VM now gets #UD (rather than #GP(0)) when it attempts to execute `vmcall` in ring 1, 2 or 3. 2. A pre-launched VM now gets #UD (rather than the return value -EPERM) when it attempts to execute a trusty hypercall in ring 0. 3. The SOS now gets the return value -EINVAL (rather than -EPERM) when it attempts to invoke a trusty hypercall. 4. A post-launched VM with trusty support now gets the return value -EINVAL (rather than #UD) when it attempts to invoke a non-trusty hypercall or an invalid hypercall. v1 -> v2: - Update documentation that describe hypercall behavior. - Fix Doxygen warnings Tracked-On: #5924 Signed-off-by: Junjie Mao <junjie.mao@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
149 lines
3.7 KiB
C
149 lines
3.7 KiB
C
/*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <types.h>
|
|
#include <errno.h>
|
|
#include <profiling.h>
|
|
#include <sbuf.h>
|
|
#include <hypercall.h>
|
|
#include <npk_log.h>
|
|
#include <asm/guest/vm.h>
|
|
#include <logmsg.h>
|
|
|
|
#ifdef PROFILING_ON
|
|
/**
|
|
* @brief Execute profiling operation
|
|
*
|
|
* @param vcpu Pointer to vCPU that initiates the hypercall
|
|
* @param param1 profiling command to be executed
|
|
* @param param2 guest physical address. This gpa points to
|
|
* data structure required by each command
|
|
*
|
|
* @pre is_sos_vm(vcpu->vm)
|
|
* @return 0 on success, non-zero on error.
|
|
*/
|
|
int32_t hcall_profiling_ops(struct acrn_vcpu *vcpu, __unused struct acrn_vm *target_vm,
|
|
uint64_t param1, uint64_t param2)
|
|
{
|
|
struct acrn_vm *vm = vcpu->vm;
|
|
int32_t ret;
|
|
uint64_t cmd = param1;
|
|
|
|
switch (cmd) {
|
|
case PROFILING_MSR_OPS:
|
|
ret = profiling_msr_ops_all_cpus(vm, param2);
|
|
break;
|
|
case PROFILING_GET_VMINFO:
|
|
ret = profiling_vm_list_info(vm, param2);
|
|
break;
|
|
case PROFILING_GET_VERSION:
|
|
ret = profiling_get_version_info(vm, param2);
|
|
break;
|
|
case PROFILING_GET_CONTROL_SWITCH:
|
|
ret = profiling_get_control(vm, param2);
|
|
break;
|
|
case PROFILING_SET_CONTROL_SWITCH:
|
|
ret = profiling_set_control(vm, param2);
|
|
break;
|
|
case PROFILING_CONFIG_PMI:
|
|
ret = profiling_configure_pmi(vm, param2);
|
|
break;
|
|
case PROFILING_CONFIG_VMSWITCH:
|
|
ret = profiling_configure_vmsw(vm, param2);
|
|
break;
|
|
case PROFILING_GET_PCPUID:
|
|
ret = profiling_get_pcpu_id(vm, param2);
|
|
break;
|
|
case PROFILING_GET_STATUS:
|
|
ret = profiling_get_status_info(vm, param2);
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid profiling command %lu\n", __func__, cmd);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* PROFILING_ON */
|
|
|
|
/**
|
|
* @brief Setup a share buffer for a VM.
|
|
*
|
|
* @param vcpu Pointer to vCPU that initiates the hypercall
|
|
* @param param1 guest physical address. This gpa points to
|
|
* struct sbuf_setup_param
|
|
*
|
|
* @pre is_sos_vm(vcpu->vm)
|
|
* @return 0 on success, non-zero on error.
|
|
*/
|
|
int32_t hcall_setup_sbuf(struct acrn_vcpu *vcpu, __unused struct acrn_vm *target_vm,
|
|
uint64_t param1, __unused uint64_t param2)
|
|
{
|
|
struct acrn_vm *vm = vcpu->vm;
|
|
struct sbuf_setup_param ssp;
|
|
uint64_t *hva;
|
|
|
|
if (copy_from_gpa(vm, &ssp, param1, sizeof(ssp)) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (ssp.gpa != 0U) {
|
|
hva = (uint64_t *)gpa2hva(vm, ssp.gpa);
|
|
} else {
|
|
hva = (uint64_t *)NULL;
|
|
}
|
|
|
|
return sbuf_share_setup(ssp.pcpu_id, ssp.sbuf_id, hva);
|
|
}
|
|
|
|
/**
|
|
* @brief Setup the hypervisor NPK log.
|
|
*
|
|
* @param vcpu Pointer to vCPU that initiates the hypercall
|
|
* @param param1 guest physical address. This gpa points to
|
|
* struct hv_npk_log_param
|
|
*
|
|
* @pre is_sos_vm(vcpu->vm)
|
|
* @return 0 on success, non-zero on error.
|
|
*/
|
|
int32_t hcall_setup_hv_npk_log(struct acrn_vcpu *vcpu, __unused struct acrn_vm *target_vm,
|
|
uint64_t param1, __unused uint64_t param2)
|
|
{
|
|
struct acrn_vm *vm = vcpu->vm;
|
|
struct hv_npk_log_param npk_param;
|
|
|
|
if (copy_from_gpa(vm, &npk_param, param1, sizeof(npk_param)) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
npk_log_setup(&npk_param);
|
|
|
|
return copy_to_gpa(vm, &npk_param, param1, sizeof(npk_param));
|
|
}
|
|
|
|
/**
|
|
* @brief Get hardware related info
|
|
*
|
|
* @param vcpu Pointer to vCPU that initiates the hypercall
|
|
* @param param1 Guest physical address pointing to struct acrn_hw_info
|
|
*
|
|
* @pre is_sos_vm(vcpu->vm)
|
|
* @pre param1 shall be a valid physical address
|
|
*
|
|
* @retval 0 on success
|
|
* @retval -1 in case of error
|
|
*/
|
|
int32_t hcall_get_hw_info(struct acrn_vcpu *vcpu, __unused struct acrn_vm *target_vm,
|
|
uint64_t param1, __unused uint64_t param2)
|
|
{
|
|
struct acrn_hw_info hw_info;
|
|
|
|
(void)memset((void *)&hw_info, 0U, sizeof(hw_info));
|
|
|
|
hw_info.cpu_num = get_pcpu_nums();
|
|
return copy_to_gpa(vcpu->vm, &hw_info, param1, sizeof(hw_info));
|
|
}
|