From patchwork Wed May 29 08:42:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Liju-clr Chen X-Patchwork-Id: 13678400 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2A7CAC25B7C for ; Wed, 29 May 2024 08:45:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Type:MIME-Version: References:In-Reply-To:Message-ID:Date:Subject:CC:To:From:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=+76nsdYqIZxrhLXsdd0cdKWzzIlYcTpkilT2PX6ZN0o=; b=g/e6bmoR4P/Xi5n9+Un0vLSja/ UzH8RaXyBA93LcuKWzBJJExK9WLUvQAl/0EjRuDCVcMYU6atS0O5yrPJyzXofVbfGlxrE6MaU6J2o H3jAhU1e36hBdL0ZxaiBX3mBOkVn7qTGVq3t2yRYzuIip961bCF8HLrz5MwUsavHbsM2VzKbqiYCB pM7resT7FITLEiENs2v/jZeZy9HW56/0sjvS69p+FJ0n43JTXMrRK8nZljoz4cWr8++Pxp0VBP1XS XtB/CctAjgEb6aZdlDRxuqwCzGwI6VrpLC36MnnEp5bAFLFIpRVmRu12eAvuVjEnec/ujYiaiaXS/ OZFiQ8sw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1sCEw3-00000003Tts-2N3C; Wed, 29 May 2024 08:45:27 +0000 Received: from mailgw02.mediatek.com ([216.200.240.185]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1sCEu8-00000003Shx-1lBT; Wed, 29 May 2024 08:43:36 +0000 X-UUID: 7ebca1a01d9711efbf6c7d4f5c147266-20240529 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=+76nsdYqIZxrhLXsdd0cdKWzzIlYcTpkilT2PX6ZN0o=; b=W6wVtxOslbtkvdqF1Fe4KTyrWYvWKLbUSGAsv30j2+5KAUxTA3NTvgq/mc8tvPTrOjVlSPoqrrzwLzuZGqlIoxPVrHUGd8rxCRaRFFAAlR4fQhn4Uz7Dbon1ILVqumTWp6jeiIcHO0R2bbQDCqA8cCNX71uX9VGqNL2HBSr6KZY=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.39,REQID:556e40a4-3f91-4802-a619-0d641cfb6b67,IP:0,U RL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:-25 X-CID-META: VersionHash:393d96e,CLOUDID:8b79f243-4544-4d06-b2b2-d7e12813c598,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:11|1,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES :1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_ULN X-UUID: 7ebca1a01d9711efbf6c7d4f5c147266-20240529 Received: from mtkmbs13n1.mediatek.inc [(172.21.101.193)] by mailgw02.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 638642081; Wed, 29 May 2024 01:43:17 -0700 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by mtkmbs11n1.mediatek.inc (172.21.101.185) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Wed, 29 May 2024 16:42:40 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Wed, 29 May 2024 16:42:40 +0800 From: Liju-clr Chen To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Catalin Marinas , Will Deacon , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , Richard Cochran , Matthias Brugger , AngeloGioacchino Del Regno , Liju-clr Chen , Yingshiuan Pan , Ze-yu Wang CC: , , , , , , , David Bradil , Trilok Soni , Shawn Hsiao , PeiLun Suei , Chi-shen Yeh , Kevenny Hsieh Subject: [PATCH v11 06/21] virt: geniezone: Add set_user_memory_region for vm Date: Wed, 29 May 2024 16:42:24 +0800 Message-ID: <20240529084239.11478-7-liju-clr.chen@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20240529084239.11478-1-liju-clr.chen@mediatek.com> References: <20240529084239.11478-1-liju-clr.chen@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240529_014328_788836_52DD4368 X-CRM114-Status: GOOD ( 27.70 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org From: Yi-De Wu From: "Yingshiuan Pan" Direct use of physical memory from VMs is forbidden and designed to be dictated to the privilege models managed by GenieZone hypervisor for security reason. With the help of gzvm-ko, the hypervisor would be able to manipulate memory as objects. And the memory management is highly integrated with ARM 2-stage translation tables to convert VA to IPA to PA under proper security measures required by protected VMs. Signed-off-by: Yingshiuan Pan Signed-off-by: Jerry Wang Signed-off-by: Liju Chen Signed-off-by: Yi-De Wu --- arch/arm64/geniezone/gzvm_arch_common.h | 2 + arch/arm64/geniezone/vm.c | 9 ++ drivers/virt/geniezone/gzvm_vm.c | 141 ++++++++++++++++++++++++ include/linux/soc/mediatek/gzvm_drv.h | 65 +++++++++++ include/uapi/linux/gzvm.h | 31 ++++++ 5 files changed, 248 insertions(+) diff --git a/arch/arm64/geniezone/gzvm_arch_common.h b/arch/arm64/geniezone/gzvm_arch_common.h index 60ee5ed2b39f..4250c0f567e7 100644 --- a/arch/arm64/geniezone/gzvm_arch_common.h +++ b/arch/arm64/geniezone/gzvm_arch_common.h @@ -11,6 +11,7 @@ enum { GZVM_FUNC_CREATE_VM = 0, GZVM_FUNC_DESTROY_VM = 1, + GZVM_FUNC_SET_MEMREGION = 4, GZVM_FUNC_PROBE = 12, NR_GZVM_FUNC, }; @@ -23,6 +24,7 @@ enum { #define MT_HVC_GZVM_CREATE_VM GZVM_HCALL_ID(GZVM_FUNC_CREATE_VM) #define MT_HVC_GZVM_DESTROY_VM GZVM_HCALL_ID(GZVM_FUNC_DESTROY_VM) +#define MT_HVC_GZVM_SET_MEMREGION GZVM_HCALL_ID(GZVM_FUNC_SET_MEMREGION) #define MT_HVC_GZVM_PROBE GZVM_HCALL_ID(GZVM_FUNC_PROBE) /** diff --git a/arch/arm64/geniezone/vm.c b/arch/arm64/geniezone/vm.c index 8ee5490d604a..d4f0aa81d224 100644 --- a/arch/arm64/geniezone/vm.c +++ b/arch/arm64/geniezone/vm.c @@ -63,6 +63,15 @@ int gzvm_arch_probe(void) return 0; } +int gzvm_arch_set_memregion(u16 vm_id, size_t buf_size, + phys_addr_t region) +{ + struct arm_smccc_res res; + + return gzvm_hypcall_wrapper(MT_HVC_GZVM_SET_MEMREGION, vm_id, + buf_size, region, 0, 0, 0, 0, &res); +} + /** * gzvm_arch_create_vm() - create vm * @vm_type: VM type. Only supports Linux VM now. diff --git a/drivers/virt/geniezone/gzvm_vm.c b/drivers/virt/geniezone/gzvm_vm.c index 76722dba6b1f..052ba236dfec 100644 --- a/drivers/virt/geniezone/gzvm_vm.c +++ b/drivers/virt/geniezone/gzvm_vm.c @@ -15,6 +15,146 @@ static DEFINE_MUTEX(gzvm_list_lock); static LIST_HEAD(gzvm_list); +int gzvm_gfn_to_hva_memslot(struct gzvm_memslot *memslot, u64 gfn, + u64 *hva_memslot) +{ + u64 offset; + + if (gfn < memslot->base_gfn) + return -EINVAL; + + offset = gfn - memslot->base_gfn; + *hva_memslot = memslot->userspace_addr + offset * PAGE_SIZE; + return 0; +} + +/** + * register_memslot_addr_range() - Register memory region to GenieZone + * @gzvm: Pointer to struct gzvm + * @memslot: Pointer to struct gzvm_memslot + * + * Return: 0 for success, negative number for error + */ +static int +register_memslot_addr_range(struct gzvm *gzvm, struct gzvm_memslot *memslot) +{ + struct gzvm_memory_region_ranges *region; + u32 buf_size = PAGE_SIZE * 2; + u64 gfn; + + region = alloc_pages_exact(buf_size, GFP_KERNEL); + if (!region) + return -ENOMEM; + + region->slot = memslot->slot_id; + region->total_pages = memslot->npages; + gfn = memslot->base_gfn; + region->gpa = PFN_PHYS(gfn); + + if (gzvm_arch_set_memregion(gzvm->vm_id, buf_size, + virt_to_phys(region))) { + pr_err("Failed to register memregion to hypervisor\n"); + free_pages_exact(region, buf_size); + return -EFAULT; + } + + free_pages_exact(region, buf_size); + return 0; +} + +/** + * memory_region_pre_check() - Preliminary check for userspace memory region + * @gzvm: Pointer to struct gzvm. + * @mem: Input memory region from user. + * + * Return: true for check passed, false for invalid input. + */ +static bool +memory_region_pre_check(struct gzvm *gzvm, + struct gzvm_userspace_memory_region *mem) +{ + if (mem->slot >= GZVM_MAX_MEM_REGION) + return false; + + if (!PAGE_ALIGNED(mem->guest_phys_addr) || + !PAGE_ALIGNED(mem->memory_size)) + return false; + + if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr) + return false; + + if ((mem->memory_size >> PAGE_SHIFT) > GZVM_MEM_MAX_NR_PAGES) + return false; + + return true; +} + +/** + * gzvm_vm_ioctl_set_memory_region() - Set memory region of guest + * @gzvm: Pointer to struct gzvm. + * @mem: Input memory region from user. + * + * Return: 0 for success, negative number for error + * + * -EXIO - The memslot is out-of-range + * -EFAULT - Cannot find corresponding vma + * -EINVAL - Region size and VMA size mismatch + */ +static int +gzvm_vm_ioctl_set_memory_region(struct gzvm *gzvm, + struct gzvm_userspace_memory_region *mem) +{ + struct vm_area_struct *vma; + struct gzvm_memslot *memslot; + unsigned long size; + + if (memory_region_pre_check(gzvm, mem) != true) + return -EINVAL; + + memslot = &gzvm->memslot[mem->slot]; + + vma = vma_lookup(gzvm->mm, mem->userspace_addr); + if (!vma) + return -EFAULT; + + size = vma->vm_end - vma->vm_start; + if (size != mem->memory_size) + return -EINVAL; + + memslot->base_gfn = __phys_to_pfn(mem->guest_phys_addr); + memslot->npages = size >> PAGE_SHIFT; + memslot->userspace_addr = mem->userspace_addr; + memslot->vma = vma; + memslot->flags = mem->flags; + memslot->slot_id = mem->slot; + return register_memslot_addr_range(gzvm, memslot); +} + +/* gzvm_vm_ioctl() - Ioctl handler of VM FD */ +static long gzvm_vm_ioctl(struct file *filp, unsigned int ioctl, + unsigned long arg) +{ + long ret; + void __user *argp = (void __user *)arg; + struct gzvm *gzvm = filp->private_data; + + switch (ioctl) { + case GZVM_SET_USER_MEMORY_REGION: { + struct gzvm_userspace_memory_region userspace_mem; + + if (copy_from_user(&userspace_mem, argp, sizeof(userspace_mem))) + return -EFAULT; + + ret = gzvm_vm_ioctl_set_memory_region(gzvm, &userspace_mem); + break; + } + default: + ret = -ENOTTY; + } + + return ret; +} + static void gzvm_destroy_vm(struct gzvm *gzvm) { pr_debug("VM-%u is going to be destroyed\n", gzvm->vm_id); @@ -42,6 +182,7 @@ static int gzvm_vm_release(struct inode *inode, struct file *filp) static const struct file_operations gzvm_vm_fops = { .release = gzvm_vm_release, + .unlocked_ioctl = gzvm_vm_ioctl, .llseek = noop_llseek, }; diff --git a/include/linux/soc/mediatek/gzvm_drv.h b/include/linux/soc/mediatek/gzvm_drv.h index e7c29c826a7c..3551de8eda1d 100644 --- a/include/linux/soc/mediatek/gzvm_drv.h +++ b/include/linux/soc/mediatek/gzvm_drv.h @@ -7,9 +7,16 @@ #define __GZVM_DRV_H__ #include +#include #include #include +/* + * For the normal physical address, the highest 12 bits should be zero, so we + * can mask bit 62 ~ bit 52 to indicate the error physical address + */ +#define GZVM_PA_ERR_BAD (0x7ffULL << 52) + #define INVALID_VM_ID 0xffff /* @@ -23,16 +30,69 @@ #define ERR_NOT_IMPLEMENTED (-27) #define ERR_FAULT (-40) +/* + * The following data structures are for data transferring between driver and + * hypervisor, and they're aligned with hypervisor definitions + */ +#define GZVM_MAX_MEM_REGION 10 + +/** + * struct mem_region_addr_range: identical to ffa memory constituent + * @address: the base IPA of the constituent memory region, aligned to 4 kiB + * @pg_cnt: the number of 4 kiB pages in the constituent memory region + * @reserved: reserved for 64bit alignment + */ +struct mem_region_addr_range { + __u64 address; + __u32 pg_cnt; + __u32 reserved; +}; + +struct gzvm_memory_region_ranges { + __u32 slot; + __u32 constituent_cnt; + __u64 total_pages; + __u64 gpa; + struct mem_region_addr_range constituents[]; +}; + +/* + * A reasonable and large enough limit for the maximum number of pages a + * guest can use. + */ +#define GZVM_MEM_MAX_NR_PAGES ((1UL << 31) - 1) + +/** + * struct gzvm_memslot: VM's memory slot descriptor + * @base_gfn: begin of guest page frame + * @npages: number of pages this slot covers + * @userspace_addr: corresponding userspace va + * @vma: vma related to this userspace addr + * @flags: define the usage of memory region. Ex. guest memory or + * firmware protection + * @slot_id: the id is used to identify the memory slot + */ +struct gzvm_memslot { + u64 base_gfn; + unsigned long npages; + unsigned long userspace_addr; + struct vm_area_struct *vma; + u32 flags; + u32 slot_id; +}; + /** * struct gzvm: the following data structures are for data transferring between * driver and hypervisor, and they're aligned with hypervisor definitions. * @mm: userspace tied to this vm + * @memslot: VM's memory slot descriptor * @lock: lock for list_add * @vm_list: list head for vm list * @vm_id: vm id */ struct gzvm { struct mm_struct *mm; + struct gzvm_memslot memslot[GZVM_MAX_MEM_REGION]; struct mutex lock; struct list_head vm_list; u16 vm_id; @@ -46,7 +106,12 @@ void gzvm_destroy_all_vms(void); /* arch-dependant functions */ int gzvm_arch_probe(void); +int gzvm_arch_set_memregion(u16 vm_id, size_t buf_size, + phys_addr_t region); int gzvm_arch_create_vm(unsigned long vm_type); int gzvm_arch_destroy_vm(u16 vm_id); +int gzvm_gfn_to_hva_memslot(struct gzvm_memslot *memslot, u64 gfn, + u64 *hva_memslot); + #endif /* __GZVM_DRV_H__ */ diff --git a/include/uapi/linux/gzvm.h b/include/uapi/linux/gzvm.h index c26c7720fab7..59c0f790b2e6 100644 --- a/include/uapi/linux/gzvm.h +++ b/include/uapi/linux/gzvm.h @@ -22,4 +22,35 @@ /* ioctls for /dev/gzvm fds */ #define GZVM_CREATE_VM _IO(GZVM_IOC_MAGIC, 0x01) /* Returns a Geniezone VM fd */ +/* ioctls for VM fds */ +/* for GZVM_SET_MEMORY_REGION */ +struct gzvm_memory_region { + __u32 slot; + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; /* bytes */ +}; + +#define GZVM_SET_MEMORY_REGION _IOW(GZVM_IOC_MAGIC, 0x40, \ + struct gzvm_memory_region) + +/** + * struct gzvm_userspace_memory_region: gzvm userspace memory region descriptor + * @slot: memory slot + * @flags: describe the usage of userspace memory region + * @guest_phys_addr: guest vm's physical address + * @memory_size: memory size in bytes + * @userspace_addr: start of the userspace allocated memory + */ +struct gzvm_userspace_memory_region { + __u32 slot; + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; + __u64 userspace_addr; +}; + +#define GZVM_SET_USER_MEMORY_REGION _IOW(GZVM_IOC_MAGIC, 0x46, \ + struct gzvm_userspace_memory_region) + #endif /* __GZVM_H__ */