ANDROID: KVM: arm64: Add a range to the guest share/unshare hypercalls
Extend the guest share and unshare HVCs with a size argument. For ABI compabitiliy with guests, a 0 range is treated as a single page, while the range argument is advertised in the meminfo HVC. Bug: 357781595 Bug: 243642516 Change-Id: Ic4927124282c430cf57f112eb96ea01740ff9967 Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
This commit is contained in:
@@ -53,8 +53,10 @@ int __pkvm_host_unshare_guest(u64 gfn, struct pkvm_hyp_vm *hyp_vm);
|
||||
int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu);
|
||||
int __pkvm_host_relax_guest_perms(u64 gfn, enum kvm_pgtable_prot prot, struct pkvm_hyp_vcpu *vcpu);
|
||||
int __pkvm_host_wrprotect_guest(u64 gfn, struct pkvm_hyp_vm *hyp_vm);
|
||||
int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa);
|
||||
int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa);
|
||||
int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa,
|
||||
u64 nr_pages, u64 *nr_shared);
|
||||
int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa,
|
||||
u64 nr_pages, u64 *nr_unshared);
|
||||
int __pkvm_install_ioguard_page(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa);
|
||||
int __pkvm_remove_ioguard_page(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa);
|
||||
bool __pkvm_check_ioguard_page(struct pkvm_hyp_vcpu *hyp_vcpu);
|
||||
|
||||
@@ -374,10 +374,6 @@ 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:
|
||||
fallthrough;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_RELINQUISH_FUNC_ID:
|
||||
n = 4;
|
||||
break;
|
||||
|
||||
@@ -1222,12 +1222,13 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa)
|
||||
int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa, u64 nr_pages,
|
||||
u64 *nr_shared)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
u64 phys, nr_pages = 1;
|
||||
kvm_pte_t pte;
|
||||
size_t size;
|
||||
u64 phys;
|
||||
int ret;
|
||||
|
||||
host_lock_component();
|
||||
@@ -1246,6 +1247,7 @@ int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa)
|
||||
WARN_ON(__guest_initiate_page_transition(ipa, pte, nr_pages, vcpu, PKVM_PAGE_SHARED_OWNED));
|
||||
WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED));
|
||||
psci_mem_protect_dec(nr_pages);
|
||||
*nr_shared = nr_pages;
|
||||
|
||||
unlock:
|
||||
guest_unlock_component(vm);
|
||||
@@ -1254,12 +1256,13 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa)
|
||||
int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa, u64 nr_pages,
|
||||
u64 *nr_unshared)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
u64 phys, nr_pages = 1;
|
||||
kvm_pte_t pte;
|
||||
size_t size;
|
||||
u64 phys;
|
||||
int ret;
|
||||
|
||||
host_lock_component();
|
||||
@@ -1278,6 +1281,7 @@ int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa)
|
||||
WARN_ON(__guest_initiate_page_transition(ipa, pte, nr_pages, vcpu, PKVM_PAGE_OWNED));
|
||||
psci_mem_protect_inc(nr_pages);
|
||||
WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_GUEST));
|
||||
*nr_unshared = nr_pages;
|
||||
|
||||
unlock:
|
||||
guest_unlock_component(vm);
|
||||
|
||||
@@ -1342,30 +1342,6 @@ 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 int pkvm_handle_empty_memcache(struct pkvm_hyp_vcpu *hyp_vcpu,
|
||||
u64 *exit_code)
|
||||
{
|
||||
@@ -1387,27 +1363,43 @@ static int pkvm_handle_empty_memcache(struct pkvm_hyp_vcpu *hyp_vcpu,
|
||||
|
||||
static bool pkvm_memshare_call(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code)
|
||||
{
|
||||
struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
|
||||
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
|
||||
u64 ipa = smccc_get_arg1(vcpu);
|
||||
u64 arg2 = smccc_get_arg2(vcpu);
|
||||
u64 nr_pages = smccc_get_arg2(vcpu);
|
||||
u64 arg3 = smccc_get_arg3(vcpu);
|
||||
struct kvm_hyp_req *req;
|
||||
u64 nr_shared;
|
||||
int err;
|
||||
|
||||
if (arg2 || arg3)
|
||||
/* Legacy guests have arg2 set to 0 */
|
||||
if (nr_pages == 0)
|
||||
nr_pages = 1;
|
||||
|
||||
if (arg3 || !PAGE_ALIGNED(ipa))
|
||||
goto out_guest_err;
|
||||
|
||||
err = __pkvm_guest_share_host(hyp_vcpu, ipa);
|
||||
err = __pkvm_guest_share_host(hyp_vcpu, ipa, nr_pages, &nr_shared);
|
||||
switch (err) {
|
||||
case 0:
|
||||
/* Success! Now tell the host. */
|
||||
goto out_host;
|
||||
atomic64_add(nr_shared * PAGE_SIZE,
|
||||
&hyp_vm->host_kvm->stat.protected_shared_mem);
|
||||
smccc_set_retval(vcpu, SMCCC_RET_SUCCESS, nr_shared, 0, 0);
|
||||
|
||||
return true;
|
||||
case -EFAULT:
|
||||
req = pkvm_hyp_req_reserve(hyp_vcpu, KVM_HYP_REQ_TYPE_MAP);
|
||||
if (!req)
|
||||
goto out_guest_err;
|
||||
|
||||
req->map.guest_ipa = ipa;
|
||||
req->map.size = nr_pages << PAGE_SHIFT;
|
||||
|
||||
/*
|
||||
* Convert the exception into a data abort so that the page
|
||||
* being shared is mapped into the guest next time.
|
||||
* We're about to go back to the host... let's not waste time
|
||||
* and check for the memcache while at it.
|
||||
*/
|
||||
*exit_code = __pkvm_memshare_page_req(hyp_vcpu, ipa);
|
||||
goto out_host;
|
||||
fallthrough;
|
||||
case -ENOMEM:
|
||||
if (pkvm_handle_empty_memcache(hyp_vcpu, exit_code))
|
||||
goto out_guest_err;
|
||||
@@ -1425,20 +1417,29 @@ out_host:
|
||||
|
||||
static bool pkvm_memunshare_call(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
|
||||
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
|
||||
u64 ipa = smccc_get_arg1(vcpu);
|
||||
u64 arg2 = smccc_get_arg2(vcpu);
|
||||
u64 nr_pages = smccc_get_arg2(vcpu);
|
||||
u64 arg3 = smccc_get_arg3(vcpu);
|
||||
u64 nr_unshared;
|
||||
int err;
|
||||
|
||||
if (arg2 || arg3)
|
||||
/* Legacy guests have arg2 set to 0 */
|
||||
if (nr_pages == 0)
|
||||
nr_pages = 1;
|
||||
|
||||
if (arg3 || !PAGE_ALIGNED(ipa))
|
||||
goto out_guest_err;
|
||||
|
||||
err = __pkvm_guest_unshare_host(hyp_vcpu, ipa);
|
||||
err = __pkvm_guest_unshare_host(hyp_vcpu, ipa, nr_pages, &nr_unshared);
|
||||
if (err)
|
||||
goto out_guest_err;
|
||||
|
||||
return false;
|
||||
atomic64_add(nr_unshared * PAGE_SIZE,
|
||||
&hyp_vm->host_kvm->stat.protected_shared_mem);
|
||||
smccc_set_retval(vcpu, SMCCC_RET_SUCCESS, nr_unshared, 0, 0);
|
||||
return true;
|
||||
|
||||
out_guest_err:
|
||||
smccc_set_retval(vcpu, SMCCC_RET_INVALID_PARAMETER, 0, 0, 0);
|
||||
@@ -1468,11 +1469,15 @@ static bool pkvm_meminfo_call(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
u64 arg1 = smccc_get_arg1(vcpu);
|
||||
u64 arg2 = smccc_get_arg2(vcpu);
|
||||
u64 arg3 = smccc_get_arg3(vcpu);
|
||||
bool has_range;
|
||||
|
||||
if (arg1 || arg2 || arg3)
|
||||
goto out_guest_err;
|
||||
|
||||
smccc_set_retval(vcpu, PAGE_SIZE, 0, 0, 0);
|
||||
has_range = smccc_get_function(vcpu) == ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID;
|
||||
|
||||
smccc_set_retval(vcpu, PAGE_SIZE,
|
||||
has_range ? KVM_FUNC_HAS_RANGE : 0, 0, 0);
|
||||
return true;
|
||||
|
||||
out_guest_err:
|
||||
|
||||
@@ -382,16 +382,6 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu)
|
||||
pkvm_host_reclaim_page(vcpu->kvm, smccc_get_arg1(vcpu));
|
||||
val[0] = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID:
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID:
|
||||
if (!kvm_vm_is_protected(vcpu->kvm))
|
||||
break;
|
||||
atomic64_add(
|
||||
func_id == ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID ?
|
||||
PAGE_SIZE : -PAGE_SIZE,
|
||||
&vcpu->kvm->stat.protected_shared_mem);
|
||||
val[0] = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
case ARM_SMCCC_TRNG_VERSION:
|
||||
case ARM_SMCCC_TRNG_FEATURES:
|
||||
case ARM_SMCCC_TRNG_GET_UUID:
|
||||
|
||||
@@ -182,6 +182,8 @@
|
||||
#define ARM_SMCCC_KVM_FUNC_FEATURES_2 127
|
||||
#define ARM_SMCCC_KVM_NUM_FUNCS 128
|
||||
|
||||
#define KVM_FUNC_HAS_RANGE BIT(0)
|
||||
|
||||
#define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_32, \
|
||||
|
||||
Reference in New Issue
Block a user