ANDROID: KVM: arm64: Expose memory sharing hypercalls to protected guests
Extend our KVM "vendor" hypercalls to expose three new hypercalls to protected guests for the purpose of opening and closing shared memory windows with the host: MEMINFO: Query the stage-2 page size (i.e. the minimum granule at which memory can be shared) MEM_SHARE: Share a page RWX with the host, faulting the page in if necessary. MEM_UNSHARE: Unshare a page with the host. Subsequent host accesses to the page will result in a fault being injected by the hypervisor. Bug: 357781595 Change-Id: I80fe8af0bc0b3a40460c5065eabe26b1d9f634f2 Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
@@ -40,7 +40,7 @@ static void handle_pvm_entry_wfx(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pvm_entry_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
static void handle_pvm_entry_psci(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
u32 psci_fn = smccc_get_function(&hyp_vcpu->vcpu);
|
||||
u64 ret = READ_ONCE(hyp_vcpu->host_vcpu->arch.ctxt.regs.regs[0]);
|
||||
@@ -77,6 +77,22 @@ static void handle_pvm_entry_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
vcpu_set_reg(&hyp_vcpu->vcpu, 0, ret);
|
||||
}
|
||||
|
||||
static void handle_pvm_entry_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
u32 fn = smccc_get_function(&hyp_vcpu->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(&hyp_vcpu->vcpu, 0, SMCCC_RET_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
handle_pvm_entry_psci(hyp_vcpu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pvm_entry_sys64(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
|
||||
@@ -234,6 +250,12 @@ static void handle_pvm_exit_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
n = 1;
|
||||
break;
|
||||
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID:
|
||||
fallthrough;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID:
|
||||
n = 4;
|
||||
break;
|
||||
|
||||
case PSCI_1_1_FN_SYSTEM_RESET2:
|
||||
case PSCI_1_1_FN64_SYSTEM_RESET2:
|
||||
n = 3;
|
||||
|
||||
@@ -1174,6 +1174,85 @@ static bool pkvm_handle_psci(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
return pvm_psci_not_supported(hyp_vcpu);
|
||||
}
|
||||
|
||||
static u64 __pkvm_memshare_page_req(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
|
||||
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 pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = &hyp_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_share_host(hyp_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(hyp_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 pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = &hyp_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(hyp_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.
|
||||
*
|
||||
@@ -1182,6 +1261,7 @@ static bool pkvm_handle_psci(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
*/
|
||||
bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
{
|
||||
u64 val[4] = { SMCCC_RET_NOT_SUPPORTED };
|
||||
u32 fn = smccc_get_function(vcpu);
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
|
||||
@@ -1190,9 +1270,37 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||
switch (fn) {
|
||||
case ARM_SMCCC_VERSION_FUNC_ID:
|
||||
/* Nothing to be handled by the host. Go back to the guest. */
|
||||
smccc_set_retval(vcpu, ARM_SMCCC_VERSION_1_1, 0, 0, 0);
|
||||
return true;
|
||||
val[0] = ARM_SMCCC_VERSION_1_1;
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID:
|
||||
val[0] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0;
|
||||
val[1] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1;
|
||||
val[2] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2;
|
||||
val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
|
||||
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(hyp_vcpu, exit_code);
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID:
|
||||
return pkvm_memunshare_call(hyp_vcpu);
|
||||
default:
|
||||
return pkvm_handle_psci(hyp_vcpu);
|
||||
}
|
||||
|
||||
smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user