ANDROID: KVM: arm64: Generalize guest PA query

Soon, pvIOMMU would want to map pages from IOVA -> PA, using IPA,
so the hypervisor would need to query the PA for this pages and
possibly request to map them, this is very similar to the logic
used in device token requests.
So, reuse the code in a more generic way that is range friendly.

Bug: 357781595
Bug: 348382247
Bug: 236685427
Change-Id: Ic0235d97a9722b75c8583dc8550025fa796eda43
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
Mostafa Saleh
2025-03-07 15:42:17 +00:00
committed by Carlos Llamas
parent ea0829d44f
commit bd55bd8408
3 changed files with 45 additions and 37 deletions
@@ -80,7 +80,9 @@ u64 __pkvm_ptdump_walk_range(pkvm_handle_t handle, struct pkvm_ptdump_log_hdr *l
int hyp_check_range_owned(u64 addr, u64 size);
int __pkvm_install_guest_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 pfn, u64 gfn);
int __pkvm_guest_get_valid_phys_page(struct pkvm_hyp_vm *vm, u64 *phys, u64 ipa);
int pkvm_get_guest_pa_request(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa,
size_t ipa_size_request, u64 *out_pa, s8 *out_level,
u64 *exit_code);
bool addr_is_memory(phys_addr_t phys);
int host_stage2_idmap_locked(phys_addr_t addr, u64 size,
+6 -24
View File
@@ -267,29 +267,6 @@ out_ret:
return ret;
}
static int pkvm_get_device_pa(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa, u64 *pa, u64 *exit_code)
{
struct kvm_hyp_req *req;
int ret;
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
ret = __pkvm_guest_get_valid_phys_page(vm, pa, ipa);
if (ret == -ENOENT) {
/* Page not mapped, create a request*/
req = pkvm_hyp_req_reserve(hyp_vcpu, KVM_HYP_REQ_TYPE_MAP);
if (!req)
return -ENOMEM;
req->map.guest_ipa = ipa;
req->map.size = PAGE_SIZE;
*exit_code = ARM_EXCEPTION_HYP_REQ;
/* Repeat next time. */
write_sysreg_el2(read_sysreg_el2(SYS_ELR) - 4, SYS_ELR);
}
return ret;
}
bool pkvm_device_request_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code)
{
int i, j, ret;
@@ -297,17 +274,22 @@ bool pkvm_device_request_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code)
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
u64 ipa = smccc_get_arg1(vcpu);
u64 token;
s8 level;
/* arg2 and arg3 reserved for future use. */
if (smccc_get_arg2(vcpu) || smccc_get_arg3(vcpu) || !PAGE_ALIGNED(ipa))
goto out_inval;
ret = pkvm_get_device_pa(hyp_vcpu, ipa, &token, exit_code);
ret = pkvm_get_guest_pa_request(hyp_vcpu, ipa, PAGE_SIZE,
&token, &level, exit_code);
if (ret == -ENOENT)
return false;
else if (ret)
goto out_inval;
/* It's expected the address is mapped as page for MMIO */
WARN_ON(level != KVM_PGTABLE_LAST_LEVEL);
hyp_spin_lock(&device_spinlock);
for (i = 0 ; i < registered_devices_nr ; ++i) {
struct pkvm_device *dev = &registered_devices[i];
+36 -12
View File
@@ -2105,18 +2105,6 @@ static int guest_get_valid_pte(struct pkvm_hyp_vm *vm, u64 *phys, u64 ipa, u8 or
return 0;
}
int __pkvm_guest_get_valid_phys_page(struct pkvm_hyp_vm *vm, u64 *phys, u64 ipa)
{
kvm_pte_t pte;
int ret;
guest_lock_component(vm);
ret = guest_get_valid_pte(vm, phys, ipa, 0, &pte);
guest_unlock_component(vm);
return ret;
}
int __pkvm_host_relax_perms_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot)
{
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
@@ -2743,6 +2731,42 @@ teardown:
return ret;
}
/* Return PA for an owned guest IPA or request it, and repeat the guest HVC */
int pkvm_get_guest_pa_request(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa,
size_t ipa_size_request, u64 *out_pa, s8 *out_level,
u64 *exit_code)
{
struct kvm_hyp_req *req;
kvm_pte_t pte;
enum pkvm_page_state state;
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
guest_lock_component(vm);
WARN_ON(kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, out_level));
guest_unlock_component(vm);
if (!kvm_pte_valid(pte)) {
/* Page not mapped, create a request*/
req = pkvm_hyp_req_reserve(hyp_vcpu, KVM_HYP_REQ_TYPE_MAP);
if (!req)
return -ENOMEM;
req->map.guest_ipa = ipa;
req->map.size = ipa_size_request;
*exit_code = ARM_EXCEPTION_HYP_REQ;
/* Repeat next time. */
write_sysreg_el2(read_sysreg_el2(SYS_ELR) - 4, SYS_ELR);
return -ENOENT;
}
state = pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte));
if (state != PKVM_PAGE_OWNED)
return -EPERM;
*out_pa = kvm_pte_to_phys(pte);
*out_pa |= (ipa & kvm_granule_size(*out_level) - 1) & PAGE_MASK;
return 0;
}
#ifdef CONFIG_PKVM_SELFTESTS
struct pkvm_expected_state {
enum pkvm_page_state host;