diff mbox series

[v16,29/34] virt: gunyah: Allow userspace to initialize context of primary vCPU

Message ID 20240109-gunyah-v16-29-634904bf4ce9@quicinc.com
State New
Headers show
Series Drivers for Gunyah hypervisor | expand

Commit Message

Elliot Berman Jan. 9, 2024, 7:38 p.m. UTC
RM provides APIs to fill boot context the initial registers upon
starting the vCPU. Most importantly, this allows userspace to set the
initial PC for the primary vCPU when the VM starts to run.

Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
---
 drivers/virt/gunyah/vm_mgr.c | 77 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/virt/gunyah/vm_mgr.h |  2 ++
 include/uapi/linux/gunyah.h  | 23 +++++++++++++
 3 files changed, 102 insertions(+)
diff mbox series

Patch

diff --git a/drivers/virt/gunyah/vm_mgr.c b/drivers/virt/gunyah/vm_mgr.c
index 3b767eeeb7c2..1f3d29749174 100644
--- a/drivers/virt/gunyah/vm_mgr.c
+++ b/drivers/virt/gunyah/vm_mgr.c
@@ -395,6 +395,7 @@  static __must_check struct gunyah_vm *gunyah_vm_alloc(struct gunyah_rm *rm)
 	mutex_init(&ghvm->resources_lock);
 	INIT_LIST_HEAD(&ghvm->resources);
 	INIT_LIST_HEAD(&ghvm->resource_tickets);
+	xa_init(&ghvm->boot_context);
 
 	mt_init(&ghvm->mm);
 	mt_init(&ghvm->bindings);
@@ -420,6 +421,66 @@  static __must_check struct gunyah_vm *gunyah_vm_alloc(struct gunyah_rm *rm)
 	return ghvm;
 }
 
+static long gunyah_vm_set_boot_context(struct gunyah_vm *ghvm,
+				       struct gunyah_vm_boot_context *boot_ctx)
+{
+	u8 reg_set, reg_index; /* to check values are reasonable */
+	int ret;
+
+	reg_set = (boot_ctx->reg >> GUNYAH_VM_BOOT_CONTEXT_REG_SHIFT) & 0xff;
+	reg_index = boot_ctx->reg & 0xff;
+
+	switch (reg_set) {
+	case REG_SET_X:
+		if (reg_index > 31)
+			return -EINVAL;
+		break;
+	case REG_SET_PC:
+		if (reg_index)
+			return -EINVAL;
+		break;
+	case REG_SET_SP:
+		if (reg_index > 2)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = down_read_interruptible(&ghvm->status_lock);
+	if (ret)
+		return ret;
+
+	if (ghvm->vm_status != GUNYAH_RM_VM_STATUS_NO_STATE) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = xa_err(xa_store(&ghvm->boot_context, boot_ctx->reg,
+			      (void *)boot_ctx->value, GFP_KERNEL));
+out:
+	up_read(&ghvm->status_lock);
+	return ret;
+}
+
+static inline int gunyah_vm_fill_boot_context(struct gunyah_vm *ghvm)
+{
+	unsigned long reg_set, reg_index, id;
+	void *entry;
+	int ret;
+
+	xa_for_each(&ghvm->boot_context, id, entry) {
+		reg_set = (id >> GUNYAH_VM_BOOT_CONTEXT_REG_SHIFT) & 0xff;
+		reg_index = id & 0xff;
+		ret = gunyah_rm_vm_set_boot_context(
+			ghvm->rm, ghvm->vmid, reg_set, reg_index, (u64)entry);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 static int gunyah_vm_start(struct gunyah_vm *ghvm)
 {
 	struct gunyah_rm_hyp_resources *resources;
@@ -496,6 +557,13 @@  static int gunyah_vm_start(struct gunyah_vm *ghvm)
 	}
 	ghvm->vm_status = GUNYAH_RM_VM_STATUS_READY;
 
+	ret = gunyah_vm_fill_boot_context(ghvm);
+	if (ret) {
+		dev_warn(ghvm->parent, "Failed to setup boot context: %d\n",
+			 ret);
+		goto err;
+	}
+
 	ret = gunyah_rm_get_hyp_resources(ghvm->rm, ghvm->vmid, &resources);
 	if (ret) {
 		dev_warn(ghvm->parent,
@@ -614,6 +682,14 @@  static long gunyah_vm_ioctl(struct file *filp, unsigned int cmd,
 
 		return gunyah_gmem_modify_mapping(ghvm, &args);
 	}
+	case GUNYAH_VM_SET_BOOT_CONTEXT: {
+		struct gunyah_vm_boot_context boot_ctx;
+
+		if (copy_from_user(&boot_ctx, argp, sizeof(boot_ctx)))
+			return -EFAULT;
+
+		return gunyah_vm_set_boot_context(ghvm, &boot_ctx);
+	}
 	default:
 		r = -ENOTTY;
 		break;
@@ -699,6 +775,7 @@  static void _gunyah_vm_put(struct kref *kref)
 				 "Failed to deallocate vmid: %d\n", ret);
 	}
 
+	xa_destroy(&ghvm->boot_context);
 	gunyah_rm_put(ghvm->rm);
 	kfree(ghvm);
 }
diff --git a/drivers/virt/gunyah/vm_mgr.h b/drivers/virt/gunyah/vm_mgr.h
index 474ac866d237..4a436c3e435c 100644
--- a/drivers/virt/gunyah/vm_mgr.h
+++ b/drivers/virt/gunyah/vm_mgr.h
@@ -79,6 +79,7 @@  long gunyah_dev_vm_mgr_ioctl(struct gunyah_rm *rm, unsigned int cmd,
  *                    folio; parcel_start is start of the folio)
  * @dtb.parcel_pages: Number of pages lent for the memory parcel
  * @dtb.parcel: Data for resource manager to lend the parcel
+ * @boot_context: Requested initial boot context to set when launching the VM
  *
  * Members are grouped by hot path.
  */
@@ -113,6 +114,7 @@  struct gunyah_vm {
 		u64 parcel_start, parcel_pages;
 		struct gunyah_rm_mem_parcel parcel;
 	} dtb;
+	struct xarray boot_context;
 };
 
 int gunyah_vm_parcel_to_paged(struct gunyah_vm *ghvm,
diff --git a/include/uapi/linux/gunyah.h b/include/uapi/linux/gunyah.h
index a89d9bedf3e5..574116f54472 100644
--- a/include/uapi/linux/gunyah.h
+++ b/include/uapi/linux/gunyah.h
@@ -142,6 +142,29 @@  struct gunyah_map_mem_args {
 
 #define GUNYAH_VM_MAP_MEM _IOW(GUNYAH_IOCTL_TYPE, 0x9, struct gunyah_map_mem_args)
 
+enum gunyah_vm_boot_context_reg {
+	REG_SET_X		= 0,
+	REG_SET_PC		= 1,
+	REG_SET_SP		= 2,
+};
+
+#define GUNYAH_VM_BOOT_CONTEXT_REG_SHIFT	8
+#define GUNYAH_VM_BOOT_CONTEXT_REG(reg, idx) (((reg & 0xff) << GUNYAH_VM_BOOT_CONTEXT_REG_SHIFT) |\
+					      (idx & 0xff))
+
+/**
+ * struct gunyah_vm_boot_context - Set an initial register for the VM
+ * @reg: Register to set. See GUNYAH_VM_BOOT_CONTEXT_REG_* macros
+ * @reserved: reserved for alignment
+ * @value: value to fill in the register
+ */
+struct gunyah_vm_boot_context {
+	__u32 reg;
+	__u32 reserved;
+	__u64 value;
+};
+#define GUNYAH_VM_SET_BOOT_CONTEXT	_IOW(GUNYAH_IOCTL_TYPE, 0xa, struct gunyah_vm_boot_context)
+
 /*
  * ioctls for vCPU fds
  */