[v6,20/21] firmware: qcom_scm: Register Gunyah platform ops

Message ID 20221026185846.3983888-21-quic_eberman@quicinc.com
State New
Headers
Series Drivers for gunyah hypervisor |

Commit Message

Elliot Berman Oct. 26, 2022, 6:58 p.m. UTC
  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.

Co-developed-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
Signed-off-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
---
 drivers/firmware/qcom_scm.c    | 114 +++++++++++++++++++++++++++++++++
 include/linux/gunyah_rsc_mgr.h |   5 ++
 2 files changed, 119 insertions(+)
  

Comments

kernel test robot Oct. 26, 2022, 9:32 p.m. UTC | #1
Hi Elliot,

I love your patch! Perhaps something to improve:

[auto build test WARNING on 247f34f7b80357943234f93f247a1ae6b6c3a740]

url:    https://github.com/intel-lab-lkp/linux/commits/Elliot-Berman/Drivers-for-gunyah-hypervisor/20221027-030321
base:   247f34f7b80357943234f93f247a1ae6b6c3a740
patch link:    https://lore.kernel.org/r/20221026185846.3983888-21-quic_eberman%40quicinc.com
patch subject: [PATCH v6 20/21] firmware: qcom_scm: Register Gunyah platform ops
config: powerpc-allyesconfig
compiler: powerpc-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/1e2d3fe903f1c461c594bd312add58369a7475cd
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Elliot-Berman/Drivers-for-gunyah-hypervisor/20221027-030321
        git checkout 1e2d3fe903f1c461c594bd312add58369a7475cd
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/firmware/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/firmware/qcom_scm.c: In function 'qcom_scm_gh_rm_pre_mem_share':
>> drivers/firmware/qcom_scm.c:1330:28: warning: left shift count >= width of type [-Wshift-count-overflow]
    1330 |                 src = (1ul << QCOM_SCM_RM_MANAGED_VMID);
         |                            ^~
   drivers/firmware/qcom_scm.c:1343:53: warning: left shift count >= width of type [-Wshift-count-overflow]
    1343 |                                         src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
         |                                                     ^~
   drivers/firmware/qcom_scm.c: In function 'qcom_scm_gh_rm_post_mem_reclaim':
   drivers/firmware/qcom_scm.c:1388:37: warning: left shift count >= width of type [-Wshift-count-overflow]
    1388 |                         src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
         |                                     ^~


vim +1330 drivers/firmware/qcom_scm.c

  1298	
  1299	static int qcom_scm_gh_rm_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel)
  1300	{
  1301		struct qcom_scm_vmperm *new_perms;
  1302		u16 this_vmid;
  1303		u64 src, src_cpy;
  1304		int ret, i, n;
  1305	
  1306		ret = gh_rm_get_vmid(&this_vmid);
  1307		if (ret)
  1308			return ret;
  1309	
  1310		new_perms = kcalloc(mem_parcel->n_acl_entries, sizeof(*new_perms), GFP_KERNEL);
  1311		if (!new_perms)
  1312			return -ENOMEM;
  1313	
  1314		for (n = 0; n < mem_parcel->n_acl_entries; n++) {
  1315			if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
  1316				new_perms[n].vmid = mem_parcel->acl_entries[n].vmid;
  1317			else
  1318				new_perms[n].vmid = QCOM_SCM_RM_MANAGED_VMID;
  1319			if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_X)
  1320				new_perms[n].perm |= QCOM_SCM_PERM_EXEC;
  1321			if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_W)
  1322				new_perms[n].perm |= QCOM_SCM_PERM_WRITE;
  1323			if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_R)
  1324				new_perms[n].perm |= QCOM_SCM_PERM_READ;
  1325		}
  1326	
  1327		if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
  1328			src = (1ul << this_vmid);
  1329		else
> 1330			src = (1ul << QCOM_SCM_RM_MANAGED_VMID);
  1331	
  1332		for (i = 0; i < mem_parcel->n_mem_entries; i++) {
  1333			src_cpy = src;
  1334			ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
  1335							mem_parcel->mem_entries[i].size,
  1336							&src_cpy, new_perms, mem_parcel->n_acl_entries);
  1337			if (ret) {
  1338				src = 0;
  1339				for (n = 0; n < mem_parcel->n_acl_entries; n++) {
  1340					if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
  1341						src |= (1ul << mem_parcel->acl_entries[n].vmid);
  1342					else
  1343						src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
  1344				}
  1345	
  1346				if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
  1347					new_perms[0].vmid = this_vmid;
  1348				else
  1349					new_perms[0].vmid = QCOM_SCM_RM_MANAGED_VMID;
  1350	
  1351				for (i--; i >= 0; i--) {
  1352					src_cpy = src;
  1353					ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
  1354									mem_parcel->mem_entries[i].size,
  1355									&src_cpy, new_perms, 1);
  1356					WARN_ON_ONCE(ret);
  1357				}
  1358				break;
  1359			}
  1360		}
  1361	
  1362		kfree(new_perms);
  1363		return ret;
  1364	}
  1365
  

Patch

diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 92763dce6477..6be7c71f8609 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -17,6 +17,7 @@ 
 #include <linux/clk.h>
 #include <linux/reset-controller.h>
 #include <linux/arm-smccc.h>
+#include <linux/gunyah_rsc_mgr.h>
 
 #include "qcom_scm.h"
 
@@ -27,6 +28,9 @@  module_param(download_mode, bool, 0);
 #define SCM_HAS_IFACE_CLK	BIT(1)
 #define SCM_HAS_BUS_CLK		BIT(2)
 
+#define QCOM_SCM_RM_MANAGED_VMID	0x3A
+#define QCOM_SCM_MAX_MANAGED_VMID	0x3F
+
 struct qcom_scm {
 	struct device *dev;
 	struct clk *core_clk;
@@ -1292,6 +1296,113 @@  int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val,
 }
 EXPORT_SYMBOL(qcom_scm_lmh_dcvsh);
 
+static int qcom_scm_gh_rm_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel)
+{
+	struct qcom_scm_vmperm *new_perms;
+	u16 this_vmid;
+	u64 src, src_cpy;
+	int ret, i, n;
+
+	ret = gh_rm_get_vmid(&this_vmid);
+	if (ret)
+		return ret;
+
+	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++) {
+		if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+			new_perms[n].vmid = mem_parcel->acl_entries[n].vmid;
+		else
+			new_perms[n].vmid = QCOM_SCM_RM_MANAGED_VMID;
+		if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_X)
+			new_perms[n].perm |= QCOM_SCM_PERM_EXEC;
+		if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_W)
+			new_perms[n].perm |= QCOM_SCM_PERM_WRITE;
+		if (mem_parcel->acl_entries[n].perms & GH_RM_ACL_R)
+			new_perms[n].perm |= QCOM_SCM_PERM_READ;
+	}
+
+	if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+		src = (1ul << this_vmid);
+	else
+		src = (1ul << QCOM_SCM_RM_MANAGED_VMID);
+
+	for (i = 0; i < mem_parcel->n_mem_entries; i++) {
+		src_cpy = src;
+		ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
+						mem_parcel->mem_entries[i].size,
+						&src_cpy, new_perms, mem_parcel->n_acl_entries);
+		if (ret) {
+			src = 0;
+			for (n = 0; n < mem_parcel->n_acl_entries; n++) {
+				if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+					src |= (1ul << mem_parcel->acl_entries[n].vmid);
+				else
+					src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
+			}
+
+			if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+				new_perms[0].vmid = this_vmid;
+			else
+				new_perms[0].vmid = QCOM_SCM_RM_MANAGED_VMID;
+
+			for (i--; i >= 0; i--) {
+				src_cpy = src;
+				ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
+								mem_parcel->mem_entries[i].size,
+								&src_cpy, new_perms, 1);
+				WARN_ON_ONCE(ret);
+			}
+			break;
+		}
+	}
+
+	kfree(new_perms);
+	return ret;
+}
+
+static int qcom_scm_gh_rm_post_mem_reclaim(struct gh_rm_mem_parcel *mem_parcel)
+{
+	struct qcom_scm_vmperm new_perms;
+	u16 this_vmid;
+	u64 src = 0;
+	int ret, i, n;
+
+
+	ret = gh_rm_get_vmid(&this_vmid);
+	if (ret)
+		return ret;
+
+	if (this_vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+		new_perms.vmid = this_vmid;
+	else
+		new_perms.vmid = QCOM_SCM_RM_MANAGED_VMID;
+	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++) {
+		if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+			src |= (1ul << mem_parcel->acl_entries[n].vmid);
+		else
+			src |= (1ul << QCOM_SCM_RM_MANAGED_VMID);
+	}
+
+	for (i = 0; i < mem_parcel->n_mem_entries; i++) {
+		ret = qcom_scm_assign_mem(mem_parcel->mem_entries[i].ipa_base,
+						mem_parcel->mem_entries[i].size,
+						&src, &new_perms, 1);
+		WARN_ON_ONCE(ret);
+	}
+
+	return ret;
+}
+
+static struct gunyah_rm_platform_ops qcom_scm_gh_rm_platform_ops = {
+	.pre_mem_share = qcom_scm_gh_rm_pre_mem_share,
+	.post_mem_reclaim = qcom_scm_gh_rm_post_mem_reclaim,
+};
+
 static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
 {
 	struct device_node *tcsr;
@@ -1414,6 +1525,9 @@  static int qcom_scm_probe(struct platform_device *pdev)
 	if (download_mode)
 		qcom_scm_set_download_mode(true);
 
+	if (gh_rm_register_platform_ops(&qcom_scm_gh_rm_platform_ops))
+		dev_warn(__scm->dev, "Gunyah RM platform ops were already registered\n");
+
 	return 0;
 }
 
diff --git a/include/linux/gunyah_rsc_mgr.h b/include/linux/gunyah_rsc_mgr.h
index 6e5e67e96688..710e9a045f02 100644
--- a/include/linux/gunyah_rsc_mgr.h
+++ b/include/linux/gunyah_rsc_mgr.h
@@ -142,6 +142,11 @@  void gh_rm_driver_unregister(struct gh_rm_driver *ghrm_drv);
 #define module_gh_rm_driver(ghrm_drv) \
 	module_driver(ghrm_drv, gh_rm_driver_register, gh_rm_driver_unregister)
 
+struct gunyah_rm_platform_ops {
+	int (*pre_mem_share)(struct gh_rm_mem_parcel *mem_parcel);
+	int (*post_mem_reclaim)(struct gh_rm_mem_parcel *mem_parcel);
+};
+
 #if IS_ENABLED(CONFIG_GUNYAH)
 int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops);
 void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops);