From patchwork Tue Jun 13 17:20:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elliot Berman X-Patchwork-Id: 13279088 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 0E4A5EB64D8 for ; Tue, 13 Jun 2023 17:22:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=qftl/MKCAqByTlLB4MYrL3zqYxdVtiV0f+C/7tlQFAc=; b=z8fnQCmWAgYnjv wq/JUpo4Rw2NBAZEGXdn0VoJrlO93ChXhNc45kWHNY+QZIAKdhHZOsb/jpMt6FTuEwmOblGs0LOfH BhrjWdiajyGBxJKFBuUUme2sGtIQUHN+VN9uJlGXd12Ohf0jPuGsOISoau9u3oLFrbrd8F13BmuVo 5MQNho4g6p1ECB0BxdWhvweNTyseF1j61SPQie/SHYN8jFlNOSvg1OgBwYK/EQwNsXlvP4yPzW0tL WJNa+kV/IjDJ8I0cxmdzArYrbDAk4X25MZizftRLqZ3n45V9qr5rR3cjfS4IgWeVQuQ2eD2J86rKb VSdQQDF2KH6rqaI0j4TQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1q97iY-008hbA-20; Tue, 13 Jun 2023 17:22:06 +0000 Received: from mx0b-0031df01.pphosted.com ([205.220.180.131]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1q97iD-008hKS-1k for linux-arm-kernel@lists.infradead.org; Tue, 13 Jun 2023 17:21:53 +0000 Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 35DDALVZ003674; Tue, 13 Jun 2023 17:21:36 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=qcppdkim1; bh=VUWFX2fucNgNUY7cBYMXyoCTosnSt+ehxC+sW4CBN1Y=; b=NJiZ5LtJ7kpCyJM4EbtSajpxqWo96NSRI3AxVQrj5tPNxFQfYM/EpVOeEPMUhc+QfDS+ o5NsvI0giXPQDJS8ZBTsYQ9eqnTM7BXfW697gn/fZpwM7t6sdINH0i71JP11SQnvNMc6 8UxXHXgtjXhl5L8qxm2IA5Nm3KrQmnD8VvJpAq9k/1zMYrIHRZHWQRM+uYdnEZlKMTMy 6B3wM0DFfTGN1CJClvTEnk4QLmTMwaDHFfbxY76Su9KPQEXBHdDh06exM66pqs+PzR4G fyej1qBAiQXotFwDer6SwfNqSsu8qhQvlboUcUNHMUG/a1wepmftdtRjH5pNfAvdgE3O mA== Received: from nasanppmta04.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3r6s3wrnb7-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 13 Jun 2023 17:21:35 +0000 Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA04.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 35DHLXHC021072 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 13 Jun 2023 17:21:33 GMT Received: from hu-eberman-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.42; Tue, 13 Jun 2023 10:21:32 -0700 From: Elliot Berman To: Alex Elder , Srinivas Kandagatla , Elliot Berman , Prakruthi Deepak Heragu CC: Murali Nalajala , Trilok Soni , Srivatsa Vaddagiri , Carl van Schaik , Dmitry Baryshkov , Bjorn Andersson , "Konrad Dybcio" , Arnd Bergmann , "Greg Kroah-Hartman" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Bagas Sanjaya , Will Deacon , Andy Gross , Catalin Marinas , Jassi Brar , , , , , Subject: [PATCH v14 10/25] gunyah: rsc_mgr: Add RPC for sharing memory Date: Tue, 13 Jun 2023 10:20:38 -0700 Message-ID: <20230613172054.3959700-11-quic_eberman@quicinc.com> X-Mailer: git-send-email 2.40.0 In-Reply-To: <20230613172054.3959700-1-quic_eberman@quicinc.com> References: <20230613172054.3959700-1-quic_eberman@quicinc.com> MIME-Version: 1.0 X-Originating-IP: [10.49.16.6] X-ClientProxiedBy: nalasex01b.na.qualcomm.com (10.47.209.197) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: n9g3J6JNjIk1OE7f4PZw5SRqx8SomJAg X-Proofpoint-GUID: n9g3J6JNjIk1OE7f4PZw5SRqx8SomJAg X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-13_19,2023-06-12_02,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 bulkscore=0 adultscore=0 mlxscore=0 mlxlogscore=999 clxscore=1015 impostorscore=0 lowpriorityscore=0 suspectscore=0 priorityscore=1501 phishscore=0 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2305260000 definitions=main-2306130153 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230613_102145_695175_896618E2 X-CRM114-Status: GOOD ( 32.77 ) X-BeenThere: linux-arm-kernel@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-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Gunyah resource manager provides API to manipulate stage 2 page tables. Manipulations are represented as a memory parcel. Memory parcels describe a list of memory regions (intermediate physical address--IPA--and size), a list of new permissions for VMs, and the memory type (DDR or MMIO). Each memory parcel is uniquely identified by a handle allocated by Gunyah. There are a few types of memory parcel sharing which Gunyah supports: - Sharing: the guest and host VM both have access - Lending: only the guest has access; host VM loses access - Donating: Permanently lent (not reclaimed even if guest shuts down) Memory parcels that have been shared or lent can be reclaimed by the host via an additional call. The reclaim operation restores the original access the host VM had to the memory parcel and removes the access to other VM. One point to note that memory parcels don't describe where in the guest VM the memory parcel should reside. The guest VM must accept the memory parcel either explicitly via a "gh_rm_mem_accept" call (not introduced here) or be configured to accept it automatically at boot. When the guest VM accepts the memory parcel, it also mentions the IPA it wants to place memory parcel. Although the region might be discontiguous on the host, the memory parcel is place contiguously in the guest memory at the specified IPA. Reviewed-by: Alex Elder Reviewed-by: Srinivas Kandagatla Co-developed-by: Prakruthi Deepak Heragu Signed-off-by: Prakruthi Deepak Heragu Signed-off-by: Elliot Berman --- drivers/virt/gunyah/rsc_mgr_rpc.c | 227 ++++++++++++++++++++++++++++++ include/linux/gunyah_rsc_mgr.h | 48 +++++++ 2 files changed, 275 insertions(+) diff --git a/drivers/virt/gunyah/rsc_mgr_rpc.c b/drivers/virt/gunyah/rsc_mgr_rpc.c index a4a9f0ba4e1fc..25064123a31c3 100644 --- a/drivers/virt/gunyah/rsc_mgr_rpc.c +++ b/drivers/virt/gunyah/rsc_mgr_rpc.c @@ -6,6 +6,12 @@ #include #include "rsc_mgr.h" +/* Message IDs: Memory Management */ +#define GH_RM_RPC_MEM_LEND 0x51000012 +#define GH_RM_RPC_MEM_SHARE 0x51000013 +#define GH_RM_RPC_MEM_RECLAIM 0x51000015 +#define GH_RM_RPC_MEM_APPEND 0x51000018 + /* Message IDs: VM Management */ #define GH_RM_RPC_VM_ALLOC_VMID 0x56000001 #define GH_RM_RPC_VM_DEALLOC_VMID 0x56000002 @@ -22,6 +28,46 @@ struct gh_rm_vm_common_vmid_req { __le16 _padding; } __packed; +/* Call: MEM_LEND, MEM_SHARE */ +#define GH_MEM_SHARE_REQ_FLAGS_APPEND BIT(1) + +struct gh_rm_mem_share_req_header { + u8 mem_type; + u8 _padding0; + u8 flags; + u8 _padding1; + __le32 label; +} __packed; + +struct gh_rm_mem_share_req_acl_section { + __le32 n_entries; + struct gh_rm_mem_acl_entry entries[]; +}; + +struct gh_rm_mem_share_req_mem_section { + __le16 n_entries; + __le16 _padding; + struct gh_rm_mem_entry entries[]; +}; + +/* Call: MEM_RELEASE */ +struct gh_rm_mem_release_req { + __le32 mem_handle; + u8 flags; /* currently not used */ + u8 _padding0; + __le16 _padding1; +} __packed; + +/* Call: MEM_APPEND */ +#define GH_MEM_APPEND_REQ_FLAGS_END BIT(0) + +struct gh_rm_mem_append_req_header { + __le32 mem_handle; + u8 flags; + u8 _padding0; + __le16 _padding1; +} __packed; + /* Call: VM_ALLOC */ struct gh_rm_vm_alloc_vmid_resp { __le16 vmid; @@ -51,6 +97,8 @@ struct gh_rm_vm_config_image_req { __le64 dtb_size; } __packed; +#define GH_RM_MAX_MEM_ENTRIES 512 + /* * Several RM calls take only a VMID as a parameter and give only standard * response back. Deduplicate boilerplate code by using this common call. @@ -64,6 +112,185 @@ static int gh_rm_common_vmid_call(struct gh_rm *rm, u32 message_id, u16 vmid) return gh_rm_call(rm, message_id, &req_payload, sizeof(req_payload), NULL, NULL); } +static int _gh_rm_mem_append(struct gh_rm *rm, u32 mem_handle, bool end_append, + struct gh_rm_mem_entry *mem_entries, size_t n_mem_entries) +{ + struct gh_rm_mem_share_req_mem_section *mem_section; + struct gh_rm_mem_append_req_header *req_header; + size_t msg_size = 0; + void *msg; + int ret; + + msg_size += sizeof(struct gh_rm_mem_append_req_header); + msg_size += struct_size(mem_section, entries, n_mem_entries); + + msg = kzalloc(msg_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + req_header = msg; + mem_section = (void *)(req_header + 1); + + req_header->mem_handle = cpu_to_le32(mem_handle); + if (end_append) + req_header->flags |= GH_MEM_APPEND_REQ_FLAGS_END; + + mem_section->n_entries = cpu_to_le16(n_mem_entries); + memcpy(mem_section->entries, mem_entries, sizeof(*mem_entries) * n_mem_entries); + + ret = gh_rm_call(rm, GH_RM_RPC_MEM_APPEND, msg, msg_size, NULL, NULL); + kfree(msg); + + return ret; +} + +static int gh_rm_mem_append(struct gh_rm *rm, u32 mem_handle, + struct gh_rm_mem_entry *mem_entries, size_t n_mem_entries) +{ + bool end_append; + int ret = 0; + size_t n; + + while (n_mem_entries) { + if (n_mem_entries > GH_RM_MAX_MEM_ENTRIES) { + end_append = false; + n = GH_RM_MAX_MEM_ENTRIES; + } else { + end_append = true; + n = n_mem_entries; + } + + ret = _gh_rm_mem_append(rm, mem_handle, end_append, mem_entries, n); + if (ret) + break; + + mem_entries += n; + n_mem_entries -= n; + } + + return ret; +} + +static int gh_rm_mem_lend_common(struct gh_rm *rm, u32 message_id, struct gh_rm_mem_parcel *p) +{ + size_t msg_size = 0, initial_mem_entries = p->n_mem_entries, resp_size; + size_t acl_section_size, mem_section_size; + struct gh_rm_mem_share_req_acl_section *acl_section; + struct gh_rm_mem_share_req_mem_section *mem_section; + struct gh_rm_mem_share_req_header *req_header; + u32 *attr_section; + __le32 *resp; + void *msg; + int ret; + + if (!p->acl_entries || !p->n_acl_entries || !p->mem_entries || !p->n_mem_entries || + p->n_acl_entries > U8_MAX || p->mem_handle != GH_MEM_HANDLE_INVAL) + return -EINVAL; + + if (initial_mem_entries > GH_RM_MAX_MEM_ENTRIES) + initial_mem_entries = GH_RM_MAX_MEM_ENTRIES; + + acl_section_size = struct_size(acl_section, entries, p->n_acl_entries); + mem_section_size = struct_size(mem_section, entries, initial_mem_entries); + /* The format of the message goes: + * request header + * ACL entries (which VMs get what kind of access to this memory parcel) + * Memory entries (list of memory regions to share) + * Memory attributes (currently unused, we'll hard-code the size to 0) + */ + msg_size += sizeof(struct gh_rm_mem_share_req_header); + msg_size += acl_section_size; + msg_size += mem_section_size; + msg_size += sizeof(u32); /* for memory attributes, currently unused */ + + msg = kzalloc(msg_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + req_header = msg; + acl_section = (void *)req_header + sizeof(*req_header); + mem_section = (void *)acl_section + acl_section_size; + attr_section = (void *)mem_section + mem_section_size; + + req_header->mem_type = p->mem_type; + if (initial_mem_entries != p->n_mem_entries) + req_header->flags |= GH_MEM_SHARE_REQ_FLAGS_APPEND; + req_header->label = cpu_to_le32(p->label); + + acl_section->n_entries = cpu_to_le32(p->n_acl_entries); + memcpy(acl_section->entries, p->acl_entries, + flex_array_size(acl_section, entries, p->n_acl_entries)); + + mem_section->n_entries = cpu_to_le16(initial_mem_entries); + memcpy(mem_section->entries, p->mem_entries, + flex_array_size(mem_section, entries, initial_mem_entries)); + + /* Set n_entries for memory attribute section to 0 */ + *attr_section = 0; + + ret = gh_rm_call(rm, message_id, msg, msg_size, (void **)&resp, &resp_size); + kfree(msg); + + if (ret) + return ret; + + p->mem_handle = le32_to_cpu(*resp); + kfree(resp); + + if (initial_mem_entries != p->n_mem_entries) { + ret = gh_rm_mem_append(rm, p->mem_handle, + &p->mem_entries[initial_mem_entries], + p->n_mem_entries - initial_mem_entries); + if (ret) { + gh_rm_mem_reclaim(rm, p); + p->mem_handle = GH_MEM_HANDLE_INVAL; + } + } + + return ret; +} + +/** + * gh_rm_mem_lend() - Lend memory to other virtual machines. + * @rm: Handle to a Gunyah resource manager + * @parcel: Information about the memory to be lent. + * + * Lending removes Linux's access to the memory while the memory parcel is lent. + */ +int gh_rm_mem_lend(struct gh_rm *rm, struct gh_rm_mem_parcel *parcel) +{ + return gh_rm_mem_lend_common(rm, GH_RM_RPC_MEM_LEND, parcel); +} + + +/** + * gh_rm_mem_share() - Share memory with other virtual machines. + * @rm: Handle to a Gunyah resource manager + * @parcel: Information about the memory to be shared. + * + * Sharing keeps Linux's access to the memory while the memory parcel is shared. + */ +int gh_rm_mem_share(struct gh_rm *rm, struct gh_rm_mem_parcel *parcel) +{ + return gh_rm_mem_lend_common(rm, GH_RM_RPC_MEM_SHARE, parcel); +} + +/** + * gh_rm_mem_reclaim() - Reclaim a memory parcel + * @rm: Handle to a Gunyah resource manager + * @parcel: Information about the memory to be reclaimed. + * + * RM maps the associated memory back into the stage-2 page tables of the owner VM. + */ +int gh_rm_mem_reclaim(struct gh_rm *rm, struct gh_rm_mem_parcel *parcel) +{ + struct gh_rm_mem_release_req req = { + .mem_handle = cpu_to_le32(parcel->mem_handle), + }; + + return gh_rm_call(rm, GH_RM_RPC_MEM_RECLAIM, &req, sizeof(req), NULL, NULL); +} + /** * gh_rm_alloc_vmid() - Allocate a new VM in Gunyah. Returns the VM identifier. * @rm: Handle to a Gunyah resource manager diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h index 1ac66d9004d2b..dfac088420bda 100644 --- a/include/linux/gunyah_rsc_mgr.h +++ b/include/linux/gunyah_rsc_mgr.h @@ -11,6 +11,7 @@ #include #define GH_VMID_INVAL U16_MAX +#define GH_MEM_HANDLE_INVAL U32_MAX struct gh_rm; int gh_rm_notifier_register(struct gh_rm *rm, struct notifier_block *nb); @@ -51,7 +52,54 @@ struct gh_rm_vm_status_payload { #define GH_RM_NOTIFICATION_VM_STATUS 0x56100008 +#define GH_RM_ACL_X BIT(0) +#define GH_RM_ACL_W BIT(1) +#define GH_RM_ACL_R BIT(2) + +struct gh_rm_mem_acl_entry { + __le16 vmid; + u8 perms; + u8 reserved; +} __packed; + +struct gh_rm_mem_entry { + __le64 phys_addr; + __le64 size; +} __packed; + +enum gh_rm_mem_type { + GH_RM_MEM_TYPE_NORMAL = 0, + GH_RM_MEM_TYPE_IO = 1, +}; + +/* + * struct gh_rm_mem_parcel - Info about memory to be lent/shared/donated/reclaimed + * @mem_type: The type of memory: normal (DDR) or IO + * @label: An client-specified identifier which can be used by the other VMs to identify the purpose + * of the memory parcel. + * @n_acl_entries: Count of the number of entries in the @acl_entries array. + * @acl_entries: An array of access control entries. Each entry specifies a VM and what access + * is allowed for the memory parcel. + * @n_mem_entries: Count of the number of entries in the @mem_entries array. + * @mem_entries: An array of regions to be associated with the memory parcel. Addresses should be + * (intermediate) physical addresses from Linux's perspective. + * @mem_handle: On success, filled with memory handle that RM allocates for this memory parcel + */ +struct gh_rm_mem_parcel { + enum gh_rm_mem_type mem_type; + u32 label; + size_t n_acl_entries; + struct gh_rm_mem_acl_entry *acl_entries; + size_t n_mem_entries; + struct gh_rm_mem_entry *mem_entries; + u32 mem_handle; +}; + /* RPC Calls */ +int gh_rm_mem_lend(struct gh_rm *rm, struct gh_rm_mem_parcel *parcel); +int gh_rm_mem_share(struct gh_rm *rm, struct gh_rm_mem_parcel *parcel); +int gh_rm_mem_reclaim(struct gh_rm *rm, struct gh_rm_mem_parcel *parcel); + int gh_rm_alloc_vmid(struct gh_rm *rm, u16 vmid); int gh_rm_dealloc_vmid(struct gh_rm *rm, u16 vmid); int gh_rm_vm_reset(struct gh_rm *rm, u16 vmid);