BACKPORT: ANDROID: KVM: arm64: Handle mem HYP_REQ in the host

The hypervisor can use a per-VCPU shared page to fill requests for the
host. Use it to fulfill the hypervisor wishes.

Bug: 458241298
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Change-Id: Id31076d511424fbf91b0e2481578bad958e35a82
(cherry picked from commit 92ba603260b542b617ab464af00b42dc98812818)
[tabba@: the base's host-side hyp_alloc top-up is pkvm_hyp_topup(PKVM_TOPUP_HYP_ALLOC),
 not A17's __pkvm_topup_hyp_alloc; add a thin pkvm_topup_hyp_alloc() wrapper over it
 and call that for REQ_MEM_DEST_HYP_ALLOC. Inert until a consumer raises
 ARM_EXCEPTION_HYP_REQ (the FF-A back-half).]
Signed-off-by: Fuad Tabba <tabba@google.com>
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index 705ebc8..c94a97d 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -25,6 +25,7 @@ enum pkvm_topup_id {
 
 unsigned long pkvm_hyp_reclaim(enum pkvm_topup_id id, unsigned long target);
 unsigned long pkvm_hyp_reclaimable(enum pkvm_topup_id id);
+int pkvm_topup_hyp_alloc(unsigned long nr_pages);
 
 int pkvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap);
 int pkvm_init_host_vm(struct kvm *kvm, unsigned long type);
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index d0eaf72..f96d59a 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -18,6 +18,7 @@
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_nested.h>
+#include <asm/kvm_pkvm.h>
 #include <asm/debug-monitors.h>
 #include <asm/stacktrace/nvhe.h>
 #include <asm/traps.h>
@@ -439,6 +440,48 @@ static int handle_trap_exceptions(struct kvm_vcpu *vcpu)
 	return handled;
 }
 
+static int handle_hyp_req_mem(struct kvm_vcpu *vcpu,
+			      struct kvm_hyp_req *req)
+{
+	switch (req->mem.dest) {
+	case REQ_MEM_DEST_HYP_ALLOC:
+		return pkvm_topup_hyp_alloc(req->mem.nr_pages);
+	case REQ_MEM_DEST_VCPU_MEMCACHE:
+		return topup_hyp_memcache(&vcpu->arch.stage2_mc,
+					  req->mem.nr_pages);
+	};
+
+	pr_warn("Unknown kvm_hyp_req mem dest: %d\n", req->mem.dest);
+
+	return -EINVAL;
+}
+
+static int handle_hyp_req(struct kvm_vcpu *vcpu)
+{
+	struct kvm_hyp_req *hyp_req = vcpu->arch.hyp_reqs;
+	int i, ret;
+
+	for (i = 0; i < KVM_HYP_REQ_MAX; i++, hyp_req++) {
+		if (hyp_req->type == KVM_HYP_LAST_REQ)
+			break;
+
+		switch (hyp_req->type) {
+		case KVM_HYP_REQ_TYPE_MEM:
+			ret = handle_hyp_req_mem(vcpu, hyp_req);
+			break;
+		default:
+			pr_warn("Unknown kvm_hyp_req type: %d\n", hyp_req->type);
+			ret = -EINVAL;
+		}
+
+		if (ret)
+			return ret;
+	}
+
+	/* handled */
+	return 1;
+}
+
 /*
  * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
  * proper exit to userspace.
@@ -478,6 +521,8 @@ int handle_exit(struct kvm_vcpu *vcpu, int exception_index)
 		 */
 		run->exit_reason = KVM_EXIT_FAIL_ENTRY;
 		return -EINVAL;
+	case ARM_EXCEPTION_HYP_REQ:
+		return handle_hyp_req(vcpu);
 	default:
 		kvm_pr_unimpl("Unsupported exception type: %d",
 			      exception_index);
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 356b822..4107aaf 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -282,6 +282,11 @@ static int pkvm_hyp_topup(enum pkvm_topup_id id, unsigned long nr_pages)
 	return res.a1;
 }
 
+int pkvm_topup_hyp_alloc(unsigned long nr_pages)
+{
+	return pkvm_hyp_topup(PKVM_TOPUP_HYP_ALLOC, nr_pages);
+}
+
 unsigned long pkvm_hyp_reclaim(enum pkvm_topup_id id, unsigned long target)
 {
 	struct kvm_hyp_memcache mc;