From 67d7d75278b34154c001ddfb435beae6bf2cd315 Mon Sep 17 00:00:00 2001 From: Prakruthi Deepak Heragu Date: Mon, 24 Feb 2025 12:10:08 -0800 Subject: [PATCH] ANDROID: gunyah: vm_mgr: Use auth_mgr_ops for a generic VM setup Gunyah supports Virtual Machines (VMs) with various authentication mechanisms. Since different authentication mechanisms require distinct operations to be performed to set up a VM, it is necessary to add ops that can support these mechanisms. Bug: 399219478 Change-Id: Ia0a6d9b71edfc59a059ddd94919a9f5b7f137855 Signed-off-by: Prakruthi Deepak Heragu --- drivers/virt/gunyah/vm_mgr.c | 226 +++++++++++++++++++++++------------ drivers/virt/gunyah/vm_mgr.h | 54 ++++++++- include/linux/gunyah.h | 28 +++++ 3 files changed, 230 insertions(+), 78 deletions(-) diff --git a/drivers/virt/gunyah/vm_mgr.c b/drivers/virt/gunyah/vm_mgr.c index d0c21028f1da..c7c736277b1f 100644 --- a/drivers/virt/gunyah/vm_mgr.c +++ b/drivers/virt/gunyah/vm_mgr.c @@ -31,6 +31,107 @@ static DEFINE_XARRAY(gunyah_vm_functions); +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_generic_pre_vm_configure(struct gunyah_vm *ghvm) +{ + /* + * VMs use dtb mem parcel as the config image parcel as they + * don't have any explicit auth image/metadata + */ + + ghvm->config_image.parcel.start = gunyah_gpa_to_gfn(ghvm->dtb.config.guest_phys_addr); + ghvm->config_image.parcel.pages = gunyah_gpa_to_gfn(ghvm->dtb.config.size); + + ghvm->config_image.image_offset = 0; + ghvm->config_image.image_size = 0; + ghvm->config_image.dtb_offset = ghvm->dtb.config.guest_phys_addr - + gunyah_gfn_to_gpa(ghvm->config_image.parcel.start); + ghvm->config_image.dtb_size = ghvm->dtb.config.size; + return 0; +} + +static int gunyah_generic_pre_vm_init(struct gunyah_vm *ghvm) +{ + int ret; + + ret = gunyah_setup_demand_paging(ghvm, 0, ULONG_MAX); + if (ret) { + dev_warn(ghvm->parent, + "Failed to set up demand paging: %d\n", ret); + return ret; + } + + ret = gunyah_rm_vm_set_address_layout( + ghvm->rm, ghvm->vmid, GUNYAH_RM_RANGE_ID_IMAGE, + gunyah_gfn_to_gpa(ghvm->config_image.parcel.start), + gunyah_gfn_to_gpa(ghvm->config_image.parcel.pages)); + if (ret) { + dev_warn(ghvm->parent, + "Failed to set location of the config image mem parcel: %d\n", ret); + return ret; + } + + return ret; +} + +static int gunyah_generic_pre_vm_start(struct gunyah_vm *ghvm) +{ + int ret; + + ret = gunyah_vm_parcel_to_paged(ghvm, &ghvm->config_image.parcel.parcel, + ghvm->config_image.parcel.start, + ghvm->config_image.parcel.pages); + if (ret) + return ret; + + if (ghvm->auth != GUNYAH_RM_VM_AUTH_NONE) + return ret; + + ret = gunyah_vm_fill_boot_context(ghvm); + if (ret) { + dev_warn(ghvm->parent, "Failed to setup boot context: %d\n", + ret); + return ret; + } + return ret; +} + +void gunyah_generic_vm_start_fail(struct gunyah_vm *ghvm) +{ + /** + * need to rollback parcel_to_paged because RM is still + * tracking the parcel + */ + gunyah_vm_mm_erase_range(ghvm, + ghvm->config_image.parcel.start, + ghvm->config_image.parcel.pages); +} + +static struct gunyah_auth_vm_mgr_ops generic_vm_ops = { + .pre_vm_configure = gunyah_generic_pre_vm_configure, + .pre_vm_init = gunyah_generic_pre_vm_init, + .pre_vm_start = gunyah_generic_pre_vm_start, + .vm_start_fail = gunyah_generic_vm_start_fail, +}; + static void gunyah_vm_put_function(struct gunyah_vm_function *fn) { module_put(fn->mod); @@ -544,6 +645,9 @@ static __must_check struct gunyah_vm *gunyah_vm_alloc(struct gunyah_rm *rm) setup_extent_ticket(ghvm, &ghvm->guest_shared_extent_ticket, GUNYAH_VM_MEM_EXTENT_GUEST_SHARED_LABEL); + ghvm->auth = GUNYAH_RM_VM_AUTH_NONE; + ghvm->auth_vm_mgr_ops = &generic_vm_ops; + return ghvm; } @@ -606,30 +710,12 @@ out: 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; struct gunyah_resource *ghrsc; int ret, i, n; + u16 vmid = 0; down_write(&ghvm->status_lock); if (ghvm->vm_status != GUNYAH_RM_VM_STATUS_NO_STATE) { @@ -642,74 +728,73 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm) if (ret) goto err; - ret = gunyah_rm_alloc_vmid(ghvm->rm, 0); + ret = gunyah_vm_pre_alloc_vmid(ghvm); + if (ret) + vmid = ret; + + ret = gunyah_rm_alloc_vmid(ghvm->rm, vmid); if (ret < 0) { gunyah_rm_notifier_unregister(ghvm->rm, &ghvm->nb); goto err; } - ghvm->vmid = ret; - ghvm->vm_status = GUNYAH_RM_VM_STATUS_LOAD; + ghvm->vmid = vmid ? vmid : ret; - ghvm->dtb.parcel.start = ghvm->dtb.config.guest_phys_addr >> PAGE_SHIFT; - ghvm->dtb.parcel.pages = ghvm->dtb.config.size >> PAGE_SHIFT; - ret = gunyah_share_parcel(ghvm, &ghvm->dtb.parcel, - &ghvm->dtb.parcel.start, - &ghvm->dtb.parcel.pages); - if (ret) { - dev_warn(ghvm->parent, - "Failed to allocate parcel for DTB: %d\n", ret); + ret = gunyah_vm_pre_vm_configure(ghvm); + if (ret) goto err; - } - if (ghvm->auth == GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM) { - ghvm->fw.parcel.start = ghvm->fw.config.guest_phys_addr >> PAGE_SHIFT; - ghvm->fw.parcel.pages = ghvm->fw.config.size >> PAGE_SHIFT; + if (ghvm->fw.config.size > 0) { + ghvm->fw.parcel.start = gunyah_gpa_to_gfn(ghvm->fw.config.guest_phys_addr); + ghvm->fw.parcel.pages = gunyah_gpa_to_gfn(ghvm->fw.config.size); ret = gunyah_share_parcel(ghvm, &ghvm->fw.parcel, &ghvm->fw.parcel.start, &ghvm->fw.parcel.pages); if (ret) { dev_warn(ghvm->parent, - "Failed to allocate parcel for FW: %d\n", ret); + "Failed to share parcel for the fw: %d\n", ret); goto err; } } + + ghvm->vm_status = GUNYAH_RM_VM_STATUS_LOAD; + + ret = gunyah_share_parcel(ghvm, &ghvm->config_image.parcel, + &ghvm->config_image.parcel.start, + &ghvm->config_image.parcel.pages); + if (ret) { + dev_warn(ghvm->parent, + "Failed to allocate parcel for the config image: %d\n", ret); + goto err; + } + ret = gunyah_rm_vm_configure(ghvm->rm, ghvm->vmid, ghvm->auth, - ghvm->dtb.parcel.parcel.mem_handle, 0, 0, - ghvm->dtb.config.guest_phys_addr - - (ghvm->dtb.parcel.start - << PAGE_SHIFT), - ghvm->dtb.config.size); + ghvm->config_image.parcel.parcel.mem_handle, + ghvm->config_image.image_offset, + ghvm->config_image.image_size, + ghvm->config_image.dtb_offset, + ghvm->config_image.dtb_size); if (ret) { dev_warn(ghvm->parent, "Failed to configure VM: %d\n", ret); goto err; } - if (ghvm->auth == GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM) { + ret = gunyah_vm_authenticate(ghvm); + if (ret) + goto err; + + if (ghvm->fw.config.size > 0) { ret = gunyah_rm_vm_set_firmware_mem(ghvm->rm, ghvm->vmid, &ghvm->fw.parcel.parcel, ghvm->fw.config.guest_phys_addr - (ghvm->fw.parcel.start << PAGE_SHIFT), ghvm->fw.config.size); if (ret) { - pr_warn("Failed to configure pVM firmware\n"); + pr_warn("Failed to configure firmware\n"); goto err; } } - ret = gunyah_setup_demand_paging(ghvm, 0, ULONG_MAX); - if (ret) { - dev_warn(ghvm->parent, - "Failed to set up demand paging: %d\n", ret); - return ret; - } - - ret = gunyah_rm_vm_set_address_layout( - ghvm->rm, ghvm->vmid, GUNYAH_RM_RANGE_ID_IMAGE, - ghvm->dtb.parcel.start << PAGE_SHIFT, - ghvm->dtb.parcel.pages << PAGE_SHIFT); - if (ret) { - dev_warn(ghvm->parent, - "Failed to set location of DTB mem parcel: %d\n", ret); + ret = gunyah_vm_pre_vm_init(ghvm); + if (ret) goto err; - } ret = gunyah_rm_vm_init(ghvm->rm, ghvm->vmid); if (ret) { @@ -719,15 +804,6 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm) } ghvm->vm_status = GUNYAH_RM_VM_STATUS_READY; - if (ghvm->auth != GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM) { - 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, @@ -747,20 +823,12 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm) gunyah_vm_add_resource(ghvm, ghrsc); } - ret = gunyah_vm_parcel_to_paged(ghvm, &ghvm->dtb.parcel.parcel, - ghvm->dtb.parcel.start, - ghvm->dtb.parcel.pages); + ret = gunyah_vm_pre_vm_start(ghvm); if (ret) goto err; ret = gunyah_rm_vm_start(ghvm->rm, ghvm->vmid); if (ret) { - /** - * need to rollback parcel_to_paged because RM is still - * tracking the parcel - */ - gunyah_vm_mm_erase_range(ghvm, ghvm->dtb.parcel.start, - ghvm->dtb.parcel.pages); dev_warn(ghvm->parent, "Failed to start VM: %d\n", ret); goto err; } @@ -770,6 +838,7 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm) return ret; err: /* gunyah_vm_free will handle releasing resources and reclaiming memory */ + gunyah_vm_start_fail(ghvm); up_write(&ghvm->status_lock); return ret; } @@ -832,11 +901,14 @@ static long gunyah_vm_ioctl(struct file *filp, unsigned int cmd, return -EFAULT; if (overflows_type(fw_config.guest_phys_addr + fw_config.size, - u64)) + u64)) return -EOVERFLOW; ghvm->fw.config = fw_config; - ghvm->auth = GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM; + /* Set new auth type only if type was not set until now */ + if (ghvm->auth == GUNYAH_RM_VM_AUTH_NONE) + ghvm->auth = GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM; + r = 0; break; } diff --git a/drivers/virt/gunyah/vm_mgr.h b/drivers/virt/gunyah/vm_mgr.h index 71ecb2308062..e360f5bef5cb 100644 --- a/drivers/virt/gunyah/vm_mgr.h +++ b/drivers/virt/gunyah/vm_mgr.h @@ -143,9 +143,14 @@ struct gunyah_vm { struct device *parent; enum gunyah_rm_vm_auth_mechanism auth; + void *auth_vm_mgr_data; + struct gunyah_auth_vm_mgr_ops *auth_vm_mgr_ops; + struct { + u64 image_offset, image_size, dtb_offset, dtb_size; + struct gunyah_vm_parcel parcel; + } config_image; struct { struct gunyah_vm_dtb_config config; - struct gunyah_vm_parcel parcel; } dtb; struct { struct gunyah_vm_firmware_config config; @@ -277,4 +282,51 @@ static inline int gunyah_cma_reclaim_parcel(struct gunyah_vm *ghvm, } #endif +/* Auth VM Manager ops */ +static inline u16 gunyah_vm_pre_alloc_vmid(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->pre_alloc_vmid) + return ghvm->auth_vm_mgr_ops->pre_alloc_vmid(ghvm); + + return 0; +} + +static inline int gunyah_vm_pre_vm_configure(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->pre_vm_configure) + return ghvm->auth_vm_mgr_ops->pre_vm_configure(ghvm); + else + return -EINVAL; +} + +static inline int gunyah_vm_authenticate(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->vm_authenticate) + return ghvm->auth_vm_mgr_ops->vm_authenticate(ghvm); + + return 0; +} + +static inline int gunyah_vm_pre_vm_init(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->pre_vm_init) + return ghvm->auth_vm_mgr_ops->pre_vm_init(ghvm); + + return 0; +} + +static inline int gunyah_vm_pre_vm_start(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->pre_vm_start) + return ghvm->auth_vm_mgr_ops->pre_vm_start(ghvm); + + return 0; +} + +static inline void gunyah_vm_start_fail(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->vm_start_fail) + return ghvm->auth_vm_mgr_ops->vm_start_fail(ghvm); +} + #endif diff --git a/include/linux/gunyah.h b/include/linux/gunyah.h index 5ff31c172617..7219647d89f0 100644 --- a/include/linux/gunyah.h +++ b/include/linux/gunyah.h @@ -21,6 +21,34 @@ struct gunyah_vm; int __must_check gunyah_vm_get(struct gunyah_vm *ghvm); void gunyah_vm_put(struct gunyah_vm *ghvm); +/** + * struct gunyah_auth_vm_mgr_ops - Auth VM Mgr helper ops + * auth_vm_mgr driver will add the specific ops to setup the VM before + * the VM starts + * @pre_alloc_vmid: QTVMs have predetermined vmids. This callback can be + * used by the auth vm mgr to request RM to assign the + * same vmid for the VM. (Optional) + * @pre_vm_configure: Fill in the arguments of VM_CONFIGURE RM call. Most of + * the auth vm mgrs would need this, as different AUTH has + * a different layout of image or metadata or dtb offsets. + * @vm_authenticate: Callback to make an RM call if it is a qtvm. (Optional) + * @pre_vm_init: Callback before RM sets up all the objects/resources + * needed by the VM. For ex, this would be when you can + * choose to setup if you want demand paged VM or not. (Optional) + * @pre_vm_start: Callback for any setup before VM start where client + * drivers can share/lend memory. (Optional) + * @start_fail: Needed when roll back is needed before auth_mgr can + * clean up at a later stage. + **/ +struct gunyah_auth_vm_mgr_ops { + u16 (*pre_alloc_vmid)(struct gunyah_vm *ghvm); + int (*pre_vm_configure)(struct gunyah_vm *ghvm); + int (*vm_authenticate)(struct gunyah_vm *ghvm); + int (*pre_vm_init)(struct gunyah_vm *ghvm); + int (*pre_vm_start)(struct gunyah_vm *ghvm); + void (*vm_start_fail)(struct gunyah_vm *ghvm); +}; + struct gunyah_vm_function_instance; /** * struct gunyah_vm_function - Represents a function type