mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
ANDROID: KVM: arm64: Add MEMINFO and {UN,}SHARE hypercalls for protected guests
Expose the __pkvm_guest_{un,}share_host() functionality to protected
guests in the form of three new hypercalls in the KVM vendor service
range:
- HYP_MEMINFO: Query the size of the sharing granule (i.e. the stage-2
page size)
- MEM_SHARE: Share a page back with the host, granting RWX permission.
- MEM_UNSHARE: Remove host access to a page previously shared with
MEM_SHARE.
Signed-off-by: Will Deacon <will@kernel.org>
Bug: 209580772
Change-Id: Ie5a1f215058df6738e1d4f357497c82b8617c765
Signed-off-by: Will Deacon <willdeacon@google.com>
This commit is contained in:
@@ -64,7 +64,7 @@ int kvm_check_pvm_sysreg_table(void);
|
||||
|
||||
void pkvm_reset_vcpu(struct kvm_vcpu *vcpu);
|
||||
|
||||
bool pkvm_handle_hvc64(struct kvm_vcpu *vcpu);
|
||||
bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code);
|
||||
|
||||
struct kvm_vcpu *pvm_mpidr_to_vcpu(struct kvm_shadow_vm *vm, unsigned long mpidr);
|
||||
|
||||
|
||||
@@ -77,6 +77,11 @@ static void handle_pvm_entry_hvc64(struct kvm_vcpu *host_vcpu, struct kvm_vcpu *
|
||||
u32 fn = smccc_get_function(shadow_vcpu);
|
||||
|
||||
switch (fn) {
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID:
|
||||
fallthrough;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID:
|
||||
vcpu_set_reg(shadow_vcpu, 0, SMCCC_RET_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
handle_pvm_entry_psci(host_vcpu, shadow_vcpu);
|
||||
break;
|
||||
@@ -258,6 +263,11 @@ static int get_num_hvc_args(struct kvm_vcpu *vcpu)
|
||||
case PSCI_0_2_FN64_CPU_SUSPEND:
|
||||
return 0;
|
||||
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID:
|
||||
fallthrough;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID:
|
||||
return 3;
|
||||
|
||||
/* The rest are either blocked or handled by hyp. */
|
||||
default:
|
||||
return -1;
|
||||
|
||||
@@ -957,7 +957,89 @@ static bool pkvm_handle_psci(struct kvm_vcpu *vcpu)
|
||||
return pvm_psci_not_supported(vcpu);
|
||||
}
|
||||
|
||||
bool pkvm_handle_hvc64(struct kvm_vcpu *vcpu)
|
||||
static u64 __pkvm_memshare_page_req(struct kvm_vcpu *vcpu, u64 ipa)
|
||||
{
|
||||
u64 elr;
|
||||
|
||||
/* Fake up a data abort (Level 3 translation fault on write) */
|
||||
vcpu->arch.fault.esr_el2 = (u32)ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT |
|
||||
ESR_ELx_WNR | ESR_ELx_FSC_FAULT |
|
||||
FIELD_PREP(ESR_ELx_FSC_LEVEL, 3);
|
||||
|
||||
/* Shuffle the IPA around into the HPFAR */
|
||||
vcpu->arch.fault.hpfar_el2 = (ipa >> 8) & HPFAR_MASK;
|
||||
|
||||
/* This is a virtual address. 0's good. Let's go with 0. */
|
||||
vcpu->arch.fault.far_el2 = 0;
|
||||
|
||||
/* Rewind the ELR so we return to the HVC once the IPA is mapped */
|
||||
elr = read_sysreg(elr_el2);
|
||||
elr -=4;
|
||||
write_sysreg(elr, elr_el2);
|
||||
|
||||
return ARM_EXCEPTION_TRAP;
|
||||
}
|
||||
|
||||
static bool pkvm_memshare_call(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
u64 ipa = smccc_get_arg1(vcpu);
|
||||
u64 arg2 = smccc_get_arg2(vcpu);
|
||||
u64 arg3 = smccc_get_arg3(vcpu);
|
||||
int err;
|
||||
|
||||
if (arg2 || arg3)
|
||||
goto out_guest_err;
|
||||
|
||||
err = __pkvm_guest_share_host(vcpu, ipa);
|
||||
switch (err) {
|
||||
case 0:
|
||||
/* Success! Now tell the host. */
|
||||
goto out_host;
|
||||
case -EFAULT:
|
||||
/*
|
||||
* Convert the exception into a data abort so that the page
|
||||
* being shared is mapped into the guest next time.
|
||||
*/
|
||||
*exit_code = __pkvm_memshare_page_req(vcpu, ipa);
|
||||
goto out_host;
|
||||
}
|
||||
|
||||
out_guest_err:
|
||||
smccc_set_retval(vcpu, SMCCC_RET_INVALID_PARAMETER, 0, 0, 0);
|
||||
return true;
|
||||
|
||||
out_host:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pkvm_memunshare_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 ipa = smccc_get_arg1(vcpu);
|
||||
u64 arg2 = smccc_get_arg2(vcpu);
|
||||
u64 arg3 = smccc_get_arg3(vcpu);
|
||||
int err;
|
||||
|
||||
if (arg2 || arg3)
|
||||
goto out_guest_err;
|
||||
|
||||
err = __pkvm_guest_unshare_host(vcpu, ipa);
|
||||
if (err)
|
||||
goto out_guest_err;
|
||||
|
||||
return false;
|
||||
|
||||
out_guest_err:
|
||||
smccc_set_retval(vcpu, SMCCC_RET_INVALID_PARAMETER, 0, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handler for protected VM HVC calls.
|
||||
*
|
||||
* Returns true if the hypervisor has handled the exit, and control should go
|
||||
* back to the guest, or false if it hasn't.
|
||||
*/
|
||||
bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
u32 fn = smccc_get_function(vcpu);
|
||||
u64 val[4] = { SMCCC_RET_NOT_SUPPORTED };
|
||||
@@ -975,7 +1057,23 @@ bool pkvm_handle_hvc64(struct kvm_vcpu *vcpu)
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
|
||||
val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
|
||||
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO);
|
||||
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_MEM_SHARE);
|
||||
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_MEM_UNSHARE);
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID:
|
||||
if (smccc_get_arg1(vcpu) ||
|
||||
smccc_get_arg2(vcpu) ||
|
||||
smccc_get_arg3(vcpu)) {
|
||||
val[0] = SMCCC_RET_INVALID_PARAMETER;
|
||||
} else {
|
||||
val[0] = PAGE_SIZE;
|
||||
}
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID:
|
||||
return pkvm_memshare_call(vcpu, exit_code);
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID:
|
||||
return pkvm_memunshare_call(vcpu);
|
||||
default:
|
||||
return pkvm_handle_psci(vcpu);
|
||||
}
|
||||
|
||||
@@ -192,17 +192,6 @@ static bool kvm_handle_pvm_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
return kvm_hyp_handle_fpsimd(vcpu, exit_code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handler for protected VM HVC calls.
|
||||
*
|
||||
* Returns true if the hypervisor has handled the exit, and control should go
|
||||
* back to the guest, or false if it hasn't.
|
||||
*/
|
||||
static bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
return pkvm_handle_hvc64(vcpu);
|
||||
}
|
||||
|
||||
static const exit_handler_fn hyp_exit_handlers[] = {
|
||||
[0 ... ESR_ELx_EC_MAX] = NULL,
|
||||
[ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32,
|
||||
|
||||
@@ -107,6 +107,9 @@
|
||||
/* KVM "vendor specific" services */
|
||||
#define ARM_SMCCC_KVM_FUNC_FEATURES 0
|
||||
#define ARM_SMCCC_KVM_FUNC_PTP 1
|
||||
#define ARM_SMCCC_KVM_FUNC_HYP_MEMINFO 2
|
||||
#define ARM_SMCCC_KVM_FUNC_MEM_SHARE 3
|
||||
#define ARM_SMCCC_KVM_FUNC_MEM_UNSHARE 4
|
||||
#define ARM_SMCCC_KVM_FUNC_FEATURES_2 127
|
||||
#define ARM_SMCCC_KVM_NUM_FUNCS 128
|
||||
|
||||
@@ -129,6 +132,24 @@
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, \
|
||||
ARM_SMCCC_KVM_FUNC_PTP)
|
||||
|
||||
#define ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_64, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, \
|
||||
ARM_SMCCC_KVM_FUNC_HYP_MEMINFO)
|
||||
|
||||
#define ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_64, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, \
|
||||
ARM_SMCCC_KVM_FUNC_MEM_SHARE)
|
||||
|
||||
#define ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_64, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, \
|
||||
ARM_SMCCC_KVM_FUNC_MEM_UNSHARE)
|
||||
|
||||
/* ptp_kvm counter type ID */
|
||||
#define KVM_PTP_VIRT_COUNTER 0
|
||||
#define KVM_PTP_PHYS_COUNTER 1
|
||||
|
||||
Reference in New Issue
Block a user