From patchwork Tue Jan 9 19:37:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elliot Berman X-Patchwork-Id: 13515287 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 kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 660A8C4707C for ; Tue, 9 Jan 2024 19:39:00 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 762F58D0011; Tue, 9 Jan 2024 14:38:15 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 6C3EF8D0010; Tue, 9 Jan 2024 14:38:15 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 519668D0011; Tue, 9 Jan 2024 14:38:15 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0010.hostedemail.com [216.40.44.10]) by kanga.kvack.org (Postfix) with ESMTP id 3C5268D0008 for ; Tue, 9 Jan 2024 14:38:15 -0500 (EST) Received: from smtpin22.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay04.hostedemail.com (Postfix) with ESMTP id 0E0201A09D2 for ; Tue, 9 Jan 2024 19:38:15 +0000 (UTC) X-FDA: 81660783750.22.481F671 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) by imf26.hostedemail.com (Postfix) with ESMTP id C090814000B for ; Tue, 9 Jan 2024 19:38:12 +0000 (UTC) Authentication-Results: imf26.hostedemail.com; dkim=pass header.d=quicinc.com header.s=qcppdkim1 header.b=PxE6TmPQ; spf=pass (imf26.hostedemail.com: domain of quic_eberman@quicinc.com designates 205.220.168.131 as permitted sender) smtp.mailfrom=quic_eberman@quicinc.com; dmarc=pass (policy=none) header.from=quicinc.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1704829092; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=zJ0o/W/Uakm8GvtvCA3LlQsDnIwUJ0OXBqD9DQx3RpM=; b=FBhzWLSrCQLxLtWJ0ZMGvvsETPckSuJyAj4lLoO/x0s9tuX91RLFbJKJjsKptTgWgbL20G U57zLSHCxnWrVEGm5cVTte7UlU6g2XBp6Av+IO3oPKNeXnbf0JAyc042+7WgmG2HqS+5+Z HZCXqFyyAtqQaXR5O+9KeZGh94pSZIU= ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1704829092; a=rsa-sha256; cv=none; b=79badr0wfnGoFswCLILbKD12IziYMXwifq6k5bJSNX1W6SU0Y+bURgok9B0GzHNlfzO3ms fVaaGAA+OHTG5pPcXkbzCXhSp/PWrsUuK4XwxOKjVXbPSnGfpBARkt/ThpktVrJASlZl2y hLEzt1TeiQ9n7fVOPkbEGR3rJ/X8tj8= ARC-Authentication-Results: i=1; imf26.hostedemail.com; dkim=pass header.d=quicinc.com header.s=qcppdkim1 header.b=PxE6TmPQ; spf=pass (imf26.hostedemail.com: domain of quic_eberman@quicinc.com designates 205.220.168.131 as permitted sender) smtp.mailfrom=quic_eberman@quicinc.com; dmarc=pass (policy=none) header.from=quicinc.com Received: from pps.filterd (m0279863.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.24/8.17.1.24) with ESMTP id 409JTQDh022746; Tue, 9 Jan 2024 19:38:03 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= from:date:subject:mime-version:content-type :content-transfer-encoding:message-id:references:in-reply-to:to :cc; s=qcppdkim1; bh=zJ0o/W/Uakm8GvtvCA3LlQsDnIwUJ0OXBqD9DQx3RpM =; b=PxE6TmPQ0ZAqV3lZMoeY5MrkWvwKQDXmMiDE9xAFpfxLEn7kbWk7B6Ew+yw 77xrhnMmgTisrmlB3pMESUVi1aECfBuEKOSgKeIvEfgNDf2FTvqd09SZ+2Os0qxJ qkjWU1fZMwCk0WSaxZ14yUG5+u9Z18pQfjPmWJiYGnXhgC2++c9stcPrcsQjDveI MWo9tKifnpa4H8jDD+PE9OohPQO+F9TaiwSq3sm/wd26HEvdxXX4B6aovZ+tfk4+ 2Ce0Brp7EVAS9LYX3XQCH4oyhtrDEEplhZTZwh55i8h61y1waJk5o4KDR2km8SQY W71GBhoaJ8AeIarZCFNVKLH9UKw== Received: from nasanppmta03.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3vh9bmggd8-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 09 Jan 2024 19:38:03 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA03.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 409Jc2uq011503 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 9 Jan 2024 19:38:02 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.1118.40; Tue, 9 Jan 2024 11:38:01 -0800 From: Elliot Berman Date: Tue, 9 Jan 2024 11:37:58 -0800 Subject: [PATCH v16 20/34] virt: gunyah: Add Qualcomm Gunyah platform ops MIME-Version: 1.0 Message-ID: <20240109-gunyah-v16-20-634904bf4ce9@quicinc.com> References: <20240109-gunyah-v16-0-634904bf4ce9@quicinc.com> In-Reply-To: <20240109-gunyah-v16-0-634904bf4ce9@quicinc.com> To: Alex Elder , Srinivas Kandagatla , Murali Nalajal , Trilok Soni , Srivatsa Vaddagiri , Carl van Schaik , Philip Derrin , Prakruthi Deepak Heragu , Jonathan Corbet , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Konrad Dybcio , Bjorn Andersson , Dmitry Baryshkov , "Fuad Tabba" , Sean Christopherson , "Andrew Morton" CC: , , , , , , Elliot Berman X-Mailer: b4 0.13-dev X-Originating-IP: [10.49.16.6] X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) 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: eZmiIfHcIKuoUH2Q9aP1F4ODLB-2XTl2 X-Proofpoint-GUID: eZmiIfHcIKuoUH2Q9aP1F4ODLB-2XTl2 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.997,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-12-09_01,2023-12-07_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 mlxscore=0 clxscore=1015 spamscore=0 priorityscore=1501 malwarescore=0 mlxlogscore=999 adultscore=0 bulkscore=0 suspectscore=0 lowpriorityscore=0 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2311290000 definitions=main-2401090158 X-Rspamd-Queue-Id: C090814000B X-Rspam-User: X-Stat-Signature: 3irss8nczhszuq5d8d94w88kfa86m87r X-Rspamd-Server: rspam03 X-HE-Tag: 1704829092-748817 X-HE-Meta: U2FsdGVkX19HDO7Uga/r8I0/c7PYJTZvife20eB0ZCN/za0mnQWF8TY0F+Ppkvg8BgK2IcOJ6CmRlaiO0AgzNV+Ctk9oKU9fB5hHMGJ0ClSFlPdaY2+95+FxuachUagYUEAWCY7NIqcouaJyfapskMY9AtLeNPMGb+eJdzRUUHvkdrkw13xdhhszUWUrzIJKwC1Xxj3seFNuHw2EtPYPFnXda3xMBdtXRlei+biy3wb6VvkEZI9A/Z92N8Fgl6ERVKMFE+k4Y+nli87I5Wcwao64IPwZ7/CcHhoObom3tO5fUY2TZNG7dH2j2kmTWC9FygGFh79w9xbOvmcksB4vfWmhOjRj1+at0zXXYBaFkzjDfDKnuD8jHp1OJGlrZGD5SDs6gW1A2qJ39uAFdjAd5imZA1HSz/XwmL6f4PK6YhsqAT3EzPEJOKln1jkRY4v5Fts1uJjLpYXM7NbGuD5bBUBAMUW7wqXaXn5CpBm1Ais28EpBWilciS9DVCT8fWqpa3WpJukAd3tgjmaGhRKLR7diqkypinUiS22UupNOM5pMCdsKmEo9Ldmcgyt8o6uuHlMYePfQUPWaKPASK/FxdhlBonx/tZlbYrosu9RVtOa8nxR/AC7wzGtZMrN+BMyS7P0XUyJMlmgR45VoS+sI9GBtwBpZPR0gfdYfzc8hxytzyDiUXZsjuTvrOHsBbvN+ZmtobNnmT3rSseR9PV3h1HxoNsA46oSImzq0X55abVwJeDDWVH00iZUiqFyBODrhm4j26qJ4Prk5GSaMtlLJo11ivGisJAJP0KwrMv7ApBFrqziCsQu9IlQFY7l4JuJI6urkx7Ll3ikU1HkdGaIXRq4z0mGuY6zwicvExtCvVhv0fTslhali4oo87DaZOurodcR2RHMPDNwEH9lYuTifrP06T7bb+qf0ZgnxjhRbSMqAn1dFOy9WJOrt/ZUzwNztKLunfwkv6en0nsKCvfw RsRWLUx/ 224mTIFgAsQSBptWJQlLwq6hronwoMLqW91GtHwU0Kb+Cae1LZRAMCtWB4OK7B0fKkFrkxBiA54t52GH/S9dCaAsJJ8zMcckfidXPYPq7CIf1yhN4uCbvyj8o/k6h9wPn42zaACxpfgtv4W3XV4b8KX+OVq6unFa5ltc86oFOeYjaYWjO1OEN4JbFfq+pBiYmcJ3/yepo1LJG4H414742cDbvYMsOKOMFwN+GWyveQZ5QotEkydOF+soQLToyBIp1YHyojd3r6WbwZOA6n5LU8+fO4kbeXYBMrK7IdGtt1UbBo5Mz34zKLNqTq9xnTLbR4Sb9RsajN7D5/OCPn2yPD+9HdUiTBBxjwInmVK0QzdCbDsfUW0ibestPthgevomDJH3xUiIwVGCsJezrHqhG58uCFkewV94LF9WUrTlB9/rt7Cv07OxoNVgJwgcrfK/+Dt9MFLZsrxdvDSKkOdcwTmvX3Q== X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: Qualcomm platforms have a firmware entity which performs access control to physical pages. Dynamically started Gunyah virtual machines use the QCOM_SCM_RM_MANAGED_VMID for access. Linux thus needs to assign access to the memory used by guest VMs. Gunyah doesn't do this operation for us since it is the current VM (typically VMID_HLOS) delegating the access and not Gunyah itself. Use the Gunyah platform ops to achieve this so that only Qualcomm platforms attempt to make the needed SCM calls. Reviewed-by: Alex Elder Co-developed-by: Prakruthi Deepak Heragu Signed-off-by: Prakruthi Deepak Heragu Signed-off-by: Elliot Berman --- drivers/virt/gunyah/Kconfig | 13 +++ drivers/virt/gunyah/Makefile | 1 + drivers/virt/gunyah/gunyah_qcom.c | 218 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) diff --git a/drivers/virt/gunyah/Kconfig b/drivers/virt/gunyah/Kconfig index 23ba523d25dc..fe2823dc48ba 100644 --- a/drivers/virt/gunyah/Kconfig +++ b/drivers/virt/gunyah/Kconfig @@ -4,6 +4,7 @@ config GUNYAH tristate "Gunyah Virtualization drivers" depends on ARM64 select GUNYAH_PLATFORM_HOOKS + imply GUNYAH_QCOM_PLATFORM if ARCH_QCOM help The Gunyah drivers are the helper interfaces that run in a guest VM such as basic inter-VM IPC and signaling mechanisms, and higher level @@ -14,3 +15,15 @@ config GUNYAH config GUNYAH_PLATFORM_HOOKS tristate + +config GUNYAH_QCOM_PLATFORM + tristate "Support for Gunyah on Qualcomm platforms" + depends on GUNYAH + select GUNYAH_PLATFORM_HOOKS + select QCOM_SCM + help + Enable support for interacting with Gunyah on Qualcomm + platforms. Interaction with Qualcomm firmware requires + extra platform-specific support. + + Say Y/M here to use Gunyah on Qualcomm platforms. diff --git a/drivers/virt/gunyah/Makefile b/drivers/virt/gunyah/Makefile index ffcde0e0ccfa..a6c6f29b887a 100644 --- a/drivers/virt/gunyah/Makefile +++ b/drivers/virt/gunyah/Makefile @@ -4,3 +4,4 @@ gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_rpc.o vm_mgr.o vm_mgr_mem.o obj-$(CONFIG_GUNYAH) += gunyah.o gunyah_rsc_mgr.o gunyah_vcpu.o obj-$(CONFIG_GUNYAH_PLATFORM_HOOKS) += gunyah_platform_hooks.o +obj-$(CONFIG_GUNYAH_QCOM_PLATFORM) += gunyah_qcom.o diff --git a/drivers/virt/gunyah/gunyah_qcom.c b/drivers/virt/gunyah/gunyah_qcom.c new file mode 100644 index 000000000000..2381d75482ca --- /dev/null +++ b/drivers/virt/gunyah/gunyah_qcom.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define QCOM_SCM_RM_MANAGED_VMID 0x3A +#define QCOM_SCM_MAX_MANAGED_VMID 0x3F + +static int +qcom_scm_gunyah_rm_pre_mem_share(struct gunyah_rm *rm, + struct gunyah_rm_mem_parcel *mem_parcel) +{ + struct qcom_scm_vmperm *new_perms __free(kfree) = NULL; + u64 src, src_cpy; + int ret = 0, i, n; + u16 vmid; + + new_perms = kcalloc(mem_parcel->n_acl_entries, sizeof(*new_perms), + GFP_KERNEL); + if (!new_perms) + return -ENOMEM; + + for (n = 0; n < mem_parcel->n_acl_entries; n++) { + vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid); + if (vmid <= QCOM_SCM_MAX_MANAGED_VMID) + new_perms[n].vmid = vmid; + else + new_perms[n].vmid = QCOM_SCM_RM_MANAGED_VMID; + if (mem_parcel->acl_entries[n].perms & GUNYAH_RM_ACL_X) + new_perms[n].perm |= QCOM_SCM_PERM_EXEC; + if (mem_parcel->acl_entries[n].perms & GUNYAH_RM_ACL_W) + new_perms[n].perm |= QCOM_SCM_PERM_WRITE; + if (mem_parcel->acl_entries[n].perms & GUNYAH_RM_ACL_R) + new_perms[n].perm |= QCOM_SCM_PERM_READ; + } + + src = BIT_ULL(QCOM_SCM_VMID_HLOS); + + for (i = 0; i < mem_parcel->n_mem_entries; i++) { + src_cpy = src; + ret = qcom_scm_assign_mem( + le64_to_cpu(mem_parcel->mem_entries[i].phys_addr), + le64_to_cpu(mem_parcel->mem_entries[i].size), &src_cpy, + new_perms, mem_parcel->n_acl_entries); + if (ret) + break; + } + + /* Did it work ok? */ + if (!ret) + return 0; + + src = 0; + for (n = 0; n < mem_parcel->n_acl_entries; n++) { + vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid); + if (vmid <= QCOM_SCM_MAX_MANAGED_VMID) + src |= BIT_ULL(vmid); + else + src |= BIT_ULL(QCOM_SCM_RM_MANAGED_VMID); + } + + new_perms[0].vmid = QCOM_SCM_VMID_HLOS; + + for (i--; i >= 0; i--) { + src_cpy = src; + WARN_ON_ONCE(qcom_scm_assign_mem( + le64_to_cpu(mem_parcel->mem_entries[i].phys_addr), + le64_to_cpu(mem_parcel->mem_entries[i].size), &src_cpy, + new_perms, 1)); + } + + return ret; +} + +static int +qcom_scm_gunyah_rm_post_mem_reclaim(struct gunyah_rm *rm, + struct gunyah_rm_mem_parcel *mem_parcel) +{ + struct qcom_scm_vmperm new_perms; + u64 src = 0, src_cpy; + int ret = 0, i, n; + u16 vmid; + + new_perms.vmid = QCOM_SCM_VMID_HLOS; + new_perms.perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE | + QCOM_SCM_PERM_READ; + + for (n = 0; n < mem_parcel->n_acl_entries; n++) { + vmid = le16_to_cpu(mem_parcel->acl_entries[n].vmid); + if (vmid <= QCOM_SCM_MAX_MANAGED_VMID) + src |= (1ull << vmid); + else + src |= (1ull << QCOM_SCM_RM_MANAGED_VMID); + } + + for (i = 0; i < mem_parcel->n_mem_entries; i++) { + src_cpy = src; + ret = qcom_scm_assign_mem( + le64_to_cpu(mem_parcel->mem_entries[i].phys_addr), + le64_to_cpu(mem_parcel->mem_entries[i].size), &src_cpy, + &new_perms, 1); + WARN_ON_ONCE(ret); + } + + return ret; +} + +static int +qcom_scm_gunyah_rm_pre_demand_page(struct gunyah_rm *rm, u16 vmid, + enum gunyah_pagetable_access access, + struct folio *folio) +{ + struct qcom_scm_vmperm new_perms[2]; + unsigned int n = 1; + u64 src; + + new_perms[0].vmid = QCOM_SCM_RM_MANAGED_VMID; + new_perms[0].perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE | + QCOM_SCM_PERM_READ; + if (access != GUNYAH_PAGETABLE_ACCESS_X && + access != GUNYAH_PAGETABLE_ACCESS_RX && + access != GUNYAH_PAGETABLE_ACCESS_RWX) { + new_perms[1].vmid = QCOM_SCM_VMID_HLOS; + new_perms[1].perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE | + QCOM_SCM_PERM_READ; + n++; + } + + src = BIT_ULL(QCOM_SCM_VMID_HLOS); + + return qcom_scm_assign_mem(__pfn_to_phys(folio_pfn(folio)), + folio_size(folio), &src, new_perms, n); +} + +static int +qcom_scm_gunyah_rm_release_demand_page(struct gunyah_rm *rm, u16 vmid, + enum gunyah_pagetable_access access, + struct folio *folio) +{ + struct qcom_scm_vmperm new_perms; + u64 src; + + new_perms.vmid = QCOM_SCM_VMID_HLOS; + new_perms.perm = QCOM_SCM_PERM_EXEC | QCOM_SCM_PERM_WRITE | + QCOM_SCM_PERM_READ; + + src = BIT_ULL(QCOM_SCM_RM_MANAGED_VMID); + + if (access != GUNYAH_PAGETABLE_ACCESS_X && + access != GUNYAH_PAGETABLE_ACCESS_RX && + access != GUNYAH_PAGETABLE_ACCESS_RWX) + src |= BIT_ULL(QCOM_SCM_VMID_HLOS); + + return qcom_scm_assign_mem(__pfn_to_phys(folio_pfn(folio)), + folio_size(folio), &src, &new_perms, 1); +} + +static struct gunyah_rm_platform_ops qcom_scm_gunyah_rm_platform_ops = { + .pre_mem_share = qcom_scm_gunyah_rm_pre_mem_share, + .post_mem_reclaim = qcom_scm_gunyah_rm_post_mem_reclaim, + .pre_demand_page = qcom_scm_gunyah_rm_pre_demand_page, + .release_demand_page = qcom_scm_gunyah_rm_release_demand_page, +}; + +/* {19bd54bd-0b37-571b-946f-609b54539de6} */ +static const uuid_t QCOM_EXT_UUID = UUID_INIT(0x19bd54bd, 0x0b37, 0x571b, 0x94, + 0x6f, 0x60, 0x9b, 0x54, 0x53, + 0x9d, 0xe6); + +#define GUNYAH_QCOM_EXT_CALL_UUID_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_VENDOR_HYP, 0x3f01) + +static bool gunyah_has_qcom_extensions(void) +{ + struct arm_smccc_res res; + uuid_t uuid; + u32 *up; + + arm_smccc_1_1_smc(GUNYAH_QCOM_EXT_CALL_UUID_ID, &res); + + up = (u32 *)&uuid.b[0]; + up[0] = lower_32_bits(res.a0); + up[1] = lower_32_bits(res.a1); + up[2] = lower_32_bits(res.a2); + up[3] = lower_32_bits(res.a3); + + return uuid_equal(&uuid, &QCOM_EXT_UUID); +} + +static int __init qcom_gunyah_platform_hooks_register(void) +{ + if (!gunyah_has_qcom_extensions()) + return -ENODEV; + + pr_info("Enabling Gunyah hooks for Qualcomm platforms.\n"); + + return gunyah_rm_register_platform_ops( + &qcom_scm_gunyah_rm_platform_ops); +} + +static void __exit qcom_gunyah_platform_hooks_unregister(void) +{ + gunyah_rm_unregister_platform_ops(&qcom_scm_gunyah_rm_platform_ops); +} + +module_init(qcom_gunyah_platform_hooks_register); +module_exit(qcom_gunyah_platform_hooks_unregister); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Platform Hooks for Gunyah"); +MODULE_LICENSE("GPL");