/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _GUNYAH_VM_MGR_PRIV_H #define _GUNYAH_VM_MGR_PRIV_H #include #include #include #include #include #include #include #include #include #include #include #include "rsc_mgr.h" struct gunyah_vm_parcel { u64 start, pages; struct gunyah_rm_mem_parcel parcel; }; struct gunyah_vm_binding { enum { VM_MEM_USER, VM_MEM_CMA } mem_type; union { u64 userspace_addr; struct { struct file *file; u32 fd; u64 offset; } cma; }; struct gunyah_vm_parcel *vm_parcel; enum { VM_MEM_SHARE, VM_MEM_LEND } share_type; u64 guest_phys_addr; u64 size; u32 flags; u32 label; }; static inline u64 gunyah_gpa_to_gfn(u64 gpa) { return gpa >> PAGE_SHIFT; } static inline u64 gunyah_gfn_to_gpa(u64 gfn) { return gfn << PAGE_SHIFT; } long gunyah_dev_vm_mgr_ioctl(struct gunyah_rm *rm, unsigned int cmd, unsigned long arg); /** * struct gunyah_vm - Main representation of a Gunyah Virtual machine * @vmid: Gunyah's VMID for this virtual machine * @mm: A maple tree of all memory that has been mapped to a VM. * Indices are guest frame numbers; entries are either folios or * RM mem parcels * @bindings: A maple tree of guest memfd bindings. Indices are guest frame * numbers; entries are &struct gunyah_gmem_binding * @bindings_lock: For serialization to @bindings * @mm_s: Userspace tied to this vm * @addrspace_ticket: Resource ticket to the capability for guest VM's * address space * @host_private_extent_ticket: Resource ticket to the capability for our * memory extent from which to lend private * memory to the guest * @host_shared_extent_ticket: Resource ticket to the capaiblity for our * memory extent from which to share memory * with the guest. Distinction with * @host_private_extent_ticket needed for * current Qualcomm platforms; on non-Qualcomm * platforms, this is the same capability ID * @guest_private_extent_ticket: Resource ticket to the capaiblity for * the guest's memory extent to lend private * memory to * @guest_shared_extent_ticket: Resource ticket to the capability for * the memory extent that represents * memory shared with the guest. * @mmio_handler_root: RB tree of MMIO handlers. * Entries are &struct gunyah_vm_io_handler * @mmio_handler_lock: Serialization of traversing @mmio_handler_root * @rm: Pointer to the resource manager struct to make RM calls * @parent: For logging * @nb: Notifier block for RM notifications * @vm_status: Current state of the VM, as last reported by RM * @vm_status_wait: Wait queue for status @vm_status changes * @status_lock: Serializing state transitions * @exit_info: Breadcrumbs why VM is not running anymore * @kref: Reference counter for VM functions * @fn_lock: Serialization addition of functions * @functions: List of &struct gunyah_vm_function_instance that have been * created by user for this VM. * @resource_lock: Serializing addition of resources and resource tickets * @resources: List of &struct gunyah_resource that are associated with this VM * @resource_tickets: List of &struct gunyah_vm_resource_ticket * @auth: Authentication mechanism to be used by resource manager when * launching the VM * @dtb: For tracking dtb configuration when launching the VM * @dtb.config: Location of the DTB in the guest memory * @dtb.parcel_start: Guest frame number where the memory parcel that we lent to * VM (DTB could start in middle of folio; we lend entire * 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. */ struct gunyah_vm { u16 vmid; struct maple_tree mm; struct maple_tree bindings; struct rw_semaphore bindings_lock; struct mm_struct *mm_s; struct gunyah_vm_resource_ticket addrspace_ticket, host_private_extent_ticket, host_shared_extent_ticket, guest_private_extent_ticket, guest_shared_extent_ticket; struct rb_root mmio_handler_root; struct rw_semaphore mmio_handler_lock; struct gunyah_rm *rm; struct notifier_block nb; enum gunyah_rm_vm_status vm_status; wait_queue_head_t vm_status_wait; struct rw_semaphore status_lock; struct gunyah_vm_exit_info exit_info; struct kref kref; struct mutex fn_lock; struct list_head functions; struct mutex resources_lock; struct list_head resources; struct list_head resource_tickets; 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; } dtb; struct { struct gunyah_vm_firmware_config config; struct gunyah_vm_parcel parcel; } fw; struct xarray boot_context; }; int gunyah_vm_mmio_write(struct gunyah_vm *ghvm, u64 addr, u32 len, u64 data); /** * folio_mmapped() - Returns true if the folio is mapped into any vma * @folio: Folio to test */ static bool folio_mmapped(struct folio *folio) { struct address_space *mapping = folio->mapping; struct vm_area_struct *vma; bool ret = false; i_mmap_lock_read(mapping); vma_interval_tree_foreach(vma, &mapping->i_mmap, folio_index(folio), folio_index(folio) + folio_nr_pages(folio)) { ret = true; break; } i_mmap_unlock_read(mapping); return ret; } /** * gunyah_folio_lend_safe() - Returns true if folio is ready to be lent to guest * @folio: Folio to prepare * * Tests if the folio is mapped anywhere outside the kernel logical map * and whether any userspace has a vma containing the folio, even if it hasn't * paged it in. We want to avoid causing fault to userspace. * If userspace doesn't have it mapped anywhere, then unmap from kernel * logical map to prevent accidental access (e.g. by load_unaligned_zeropad) */ static inline bool gunyah_folio_lend_safe(struct folio *folio) { long i; if (folio_mapped(folio) || folio_mmapped(folio)) return false; for (i = 0; i < folio_nr_pages(folio); i++) set_direct_map_invalid_noflush(folio_page(folio, i)); /** * No need to flush tlb on armv8/9: hypervisor will flush when it * removes from our stage 2 */ return true; } /** * gunyah_folio_host_reclaim() - Restores kernel logical map to folio * @folio: folio to reclaim by host * * See also gunyah_folio_lend_safe(). */ static inline void gunyah_folio_host_reclaim(struct folio *folio) { long i; for (i = 0; i < folio_nr_pages(folio); i++) set_direct_map_default_noflush(folio_page(folio, i)); } int gunyah_vm_parcel_to_paged(struct gunyah_vm *ghvm, struct gunyah_rm_mem_parcel *parcel, u64 gfn, u64 nr); void gunyah_vm_mm_erase_range(struct gunyah_vm *ghvm, u64 gfn, u64 nr); int gunyah_vm_provide_folio(struct gunyah_vm *ghvm, struct folio *folio, u64 gfn, bool share, bool write); int gunyah_vm_reclaim_folio(struct gunyah_vm *ghvm, u64 gfn, struct folio *folio); int gunyah_vm_reclaim_range(struct gunyah_vm *ghvm, u64 gfn, u64 nr); int gunyah_vm_binding_alloc(struct gunyah_vm *ghvm, struct gunyah_userspace_memory_region *region, bool lend); int gunyah_share_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *parcel, u64 *gfn, u64 *nr); int gunyah_share_range_as_parcels(struct gunyah_vm *ghvm, u64 start_addr, u64 end_addr, struct gunyah_vm_parcel **parcels); int gunyah_reclaim_parcels(struct gunyah_vm *ghvm, u64 start_gfn, u64 end_gfn); int gunyah_demand_page(struct gunyah_vm *ghvm, u64 gpa, bool write); int gunyah_setup_demand_paging(struct gunyah_vm *ghvm, u64 start_gfn, u64 end_gfn); #ifdef CONFIG_DMA_CMA int gunyah_cma_mem_init(void); void gunyah_cma_mem_exit(void); int gunyah_vm_binding_cma_alloc(struct gunyah_vm *ghvm, struct gunyah_map_cma_mem_args *cma_map); int gunyah_cma_share_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *parcel, struct gunyah_vm_binding *b, u64 *gfn, u64 *nr); int gunyah_cma_reclaim_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *parcel, struct gunyah_vm_binding *b); #else static inline int gunyah_cma_mem_init(void) { return -EOPNOTSUPP; } static inline void gunyah_cma_mem_exit(void) { } static inline int gunyah_vm_binding_cma_alloc(struct gunyah_vm *ghvm, struct gunyah_map_cma_mem_args *cma_map) { return -ENOSYS; } static inline int gunyah_cma_share_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *parcel, struct gunyah_vm_binding *b, u64 *gfn, u64 *nr) { return -EINVAL; } static inline int gunyah_cma_reclaim_parcel(struct gunyah_vm *ghvm, struct gunyah_vm_parcel *parcel, struct gunyah_vm_binding *b) { return -EINVAL; } #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); } static inline int gunyah_vm_pre_vm_reset(struct gunyah_vm *ghvm) { if (ghvm->auth_vm_mgr_ops->pre_vm_reset) return ghvm->auth_vm_mgr_ops->pre_vm_reset(ghvm); return 0; } static inline int gunyah_vm_post_vm_reset(struct gunyah_vm *ghvm) { if (ghvm->auth_vm_mgr_ops->post_vm_reset) return ghvm->auth_vm_mgr_ops->post_vm_reset(ghvm); return 0; } #endif