diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index af89688f2a2b..31f64b2a47ed 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -71,6 +71,8 @@ int __pkvm_host_lazy_pte(u64 pfn, u64 nr_pages, bool enable); u64 __pkvm_ptdump_get_config(pkvm_handle_t handle, enum pkvm_ptdump_ops op); u64 __pkvm_ptdump_walk_range(pkvm_handle_t handle, struct pkvm_ptdump_log_hdr *log_hva); +int pkvm_hyp_donate_guest(struct pkvm_hyp_vcpu *vcpu, u64 pfn, u64 gfn); + bool addr_is_memory(phys_addr_t phys); int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot, diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index ad761cad44ee..1e28a4597080 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -565,6 +565,18 @@ static enum kvm_pgtable_prot default_hyp_prot(phys_addr_t phys) return addr_is_memory(phys) ? PAGE_HYP : PAGE_HYP_DEVICE; } +/* + * Use NORMAL_NC for guest MMIO, when a guest has: + * No FWB: It will combined with stage-1 attrs where device has precedence over normal. + * FWB: With MT_S2_FWB_NORMAL_NC encoding, results in device if stage-1 used device attr. + * otherwise NC. + */ +static enum kvm_pgtable_prot default_guest_prot(bool is_memory) +{ + return is_memory ? KVM_PGTABLE_PROT_RWX : + KVM_PGTABLE_PROT_RW | KVM_PGTABLE_PROT_NORMAL_NC; +} + bool addr_is_memory(phys_addr_t phys) { struct kvm_mem_range range; @@ -1387,6 +1399,38 @@ int ___pkvm_host_donate_hyp(u64 pfn, u64 nr_pages, bool accept_mmio) default_hyp_prot(hyp_pfn_to_phys(pfn))); } +int pkvm_hyp_donate_guest(struct pkvm_hyp_vcpu *vcpu, u64 pfn, u64 gfn) +{ + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + u64 phys = hyp_pfn_to_phys(pfn); + u64 ipa = hyp_pfn_to_phys(gfn); + u64 hyp_addr = (u64)__hyp_va(phys); + size_t size = PAGE_SIZE; + enum kvm_pgtable_prot prot; + int ret; + + hyp_lock_component(); + guest_lock_component(vm); + + ret = __hyp_check_page_state_range(hyp_addr, size, PKVM_PAGE_OWNED); + if (ret) + goto unlock; + ret = __guest_check_page_state_range(vcpu, ipa, size, PKVM_NOPAGE); + if (ret) + goto unlock; + + WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, hyp_addr, size) != size); + prot = pkvm_mkstate(default_guest_prot(addr_is_memory(phys)), PKVM_PAGE_OWNED); + WARN_ON(kvm_pgtable_stage2_map(&vm->pgt, ipa, size, phys, prot, + &vcpu->vcpu.arch.stage2_mc, 0)); + +unlock: + guest_unlock_component(vm); + hyp_unlock_component(); + + return ret; +} + int __pkvm_host_donate_hyp_locked(u64 pfn, u64 nr_pages, enum kvm_pgtable_prot prot) { u64 size, phys = hyp_pfn_to_phys(pfn);