ANDROID: KVM: arm64: Encode hyp requests in HVC return

For EL2 drivers like IOMMU ENOMEM might mean different things, as the
driver uses multiple allocators, instead of guessing or using an
external shared data for that, encode a hyp request in the rest of the
SMCCC return registers, we can add many other information as nr_pages,
size of the allocation(pages), and how many are needed.

Bug: 277989609
Bug: 278749606

Change-Id: I7e261ab49f29a7001965e4d40d704eba7634eb94
Signed-off-by: Mostafa Saleh <smostafa@google.com>
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 1cf1681b..f8241a2 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -730,6 +730,25 @@ struct kvm_hyp_req {
 };
 
 #define KVM_HYP_REQ_MAX (PAGE_SIZE / sizeof(struct kvm_hyp_req))
+/*
+ * De-serialize request from SMCCC return.
+ * See hyp-main.c for serialization.
+ */
+/* Register a2. */
+#define	SMCCC_REQ_TYPE_MASK		GENMASK_ULL(7, 0)
+#define SMCCC_REQ_DEST_MASK		GENMASK_ULL(15, 8)
+/* Register a3. */
+#define SMCCC_REQ_NR_PAGES_MASK		GENMASK_ULL(31, 0)
+#define SMCCC_REQ_SZ_ALLOC_MASK		GENMASK_ULL(63, 32)
+
+static inline void hyp_reqs_smccc_decode(struct arm_smccc_res *res,
+					 struct kvm_hyp_req *req)
+{
+	req->type = FIELD_GET(SMCCC_REQ_TYPE_MASK, res->a2);
+	req->mem.dest = FIELD_GET(SMCCC_REQ_DEST_MASK, res->a2);
+	req->mem.nr_pages = FIELD_GET(SMCCC_REQ_NR_PAGES_MASK, res->a3);
+	req->mem.sz_alloc = FIELD_GET(SMCCC_REQ_SZ_ALLOC_MASK, res->a3);
+}
 
 struct kvm_vcpu_arch {
 	struct kvm_cpu_context ctxt;
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index f646353..08c0ff8 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -33,6 +33,33 @@
 
 DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
 
+/*
+ * Holds one request only, in theory we can compress more, but
+ * typically HVC returns on first failure.
+ */
+DEFINE_PER_CPU(struct kvm_hyp_req, host_hyp_reqs);
+
+/* Serialize request in SMCCC return context. */
+static inline void hyp_reqs_smccc_encode(unsigned long ret, struct kvm_cpu_context *host_ctxt,
+					 struct kvm_hyp_req *req)
+{
+	cpu_reg(host_ctxt, 1) = ret;
+	cpu_reg(host_ctxt, 2) = 0;
+	cpu_reg(host_ctxt, 3) = 0;
+
+	if (req->type == KVM_HYP_REQ_TYPE_MEM) {
+		cpu_reg(host_ctxt, 2) = FIELD_PREP(SMCCC_REQ_TYPE_MASK, req->type) |
+					FIELD_PREP(SMCCC_REQ_DEST_MASK, req->mem.dest);
+
+		cpu_reg(host_ctxt, 3) = FIELD_PREP(SMCCC_REQ_NR_PAGES_MASK, req->mem.nr_pages) |
+					FIELD_PREP(SMCCC_REQ_SZ_ALLOC_MASK, req->mem.sz_alloc);
+	}
+
+	/* We can't encode others */
+	WARN_ON((req->type != KVM_HYP_REQ_TYPE_MEM) && ((req->type != KVM_HYP_LAST_REQ)));
+	req->type = KVM_HYP_LAST_REQ;
+}
+
 void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);
 
 static bool (*default_host_smc_handler)(struct user_pt_regs *regs);