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:
Will Deacon
2021-11-25 14:13:32 +00:00
committed by Will Deacon
parent 82d27dc4c8
commit e2fe2a2d52
5 changed files with 131 additions and 13 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -104,6 +104,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
@@ -126,6 +129,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