[v8,19/28] firmware: qcom_scm: Register Gunyah platform ops

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

Commit Message

Elliot Berman Dec. 19, 2022, 10: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/Kconfig    |  2 +
 drivers/firmware/qcom_scm.c | 95 +++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+)
  

Comments

kernel test robot Dec. 23, 2022, 5:21 p.m. UTC | #1
Hi Elliot,

I love your patch! Perhaps something to improve:

[auto build test WARNING on 830b3c68c1fb1e9176028d02ef86f3cf76aa2476]

url:    https://github.com/intel-lab-lkp/linux/commits/Elliot-Berman/Drivers-for-gunyah-hypervisor/20221220-073053
base:   830b3c68c1fb1e9176028d02ef86f3cf76aa2476
patch link:    https://lore.kernel.org/r/20221219225850.2397345-20-quic_eberman%40quicinc.com
patch subject: [PATCH v8 19/28] firmware: qcom_scm: Register Gunyah platform ops
config: sparc-randconfig-s032-20221218
compiler: sparc64-linux-gcc (GCC) 12.1.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.4-39-gce1a6720-dirty
        # https://github.com/intel-lab-lkp/linux/commit/6392217a770e8f1e373e8d365fd9dcce58d8eeb3
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Elliot-Berman/Drivers-for-gunyah-hypervisor/20221220-073053
        git checkout 6392217a770e8f1e373e8d365fd9dcce58d8eeb3
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=sparc olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=sparc SHELL=/bin/bash drivers/firmware/

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

sparse warnings: (new ones prefixed by >>)
>> drivers/firmware/qcom_scm.c:1310:47: sparse: sparse: restricted __le16 degrades to integer
>> drivers/firmware/qcom_scm.c:1311:43: sparse: sparse: incorrect type in assignment (different base types) @@     expected int vmid @@     got restricted __le16 [usertype] vmid @@
   drivers/firmware/qcom_scm.c:1311:43: sparse:     expected int vmid
   drivers/firmware/qcom_scm.c:1311:43: sparse:     got restricted __le16 [usertype] vmid
>> drivers/firmware/qcom_scm.c:1326:69: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned long long [usertype] mem_addr @@     got restricted __le64 [usertype] ipa_base @@
   drivers/firmware/qcom_scm.c:1326:69: sparse:     expected unsigned long long [usertype] mem_addr
   drivers/firmware/qcom_scm.c:1326:69: sparse:     got restricted __le64 [usertype] ipa_base
>> drivers/firmware/qcom_scm.c:1327:75: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected unsigned long [usertype] mem_sz @@     got restricted __le64 [usertype] size @@
   drivers/firmware/qcom_scm.c:1327:75: sparse:     expected unsigned long [usertype] mem_sz
   drivers/firmware/qcom_scm.c:1327:75: sparse:     got restricted __le64 [usertype] size
   drivers/firmware/qcom_scm.c:1332:63: sparse: sparse: restricted __le16 degrades to integer
   drivers/firmware/qcom_scm.c:1333:83: sparse: sparse: restricted __le16 degrades to integer
   drivers/firmware/qcom_scm.c:1342:85: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned long long [usertype] mem_addr @@     got restricted __le64 [usertype] ipa_base @@
   drivers/firmware/qcom_scm.c:1342:85: sparse:     expected unsigned long long [usertype] mem_addr
   drivers/firmware/qcom_scm.c:1342:85: sparse:     got restricted __le64 [usertype] ipa_base
   drivers/firmware/qcom_scm.c:1343:91: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected unsigned long [usertype] mem_sz @@     got restricted __le64 [usertype] size @@
   drivers/firmware/qcom_scm.c:1343:91: sparse:     expected unsigned long [usertype] mem_sz
   drivers/firmware/qcom_scm.c:1343:91: sparse:     got restricted __le64 [usertype] size
   drivers/firmware/qcom_scm.c:1366:47: sparse: sparse: restricted __le16 degrades to integer
   drivers/firmware/qcom_scm.c:1367:67: sparse: sparse: restricted __le16 degrades to integer
   drivers/firmware/qcom_scm.c:1373:69: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected unsigned long long [usertype] mem_addr @@     got restricted __le64 [usertype] ipa_base @@
   drivers/firmware/qcom_scm.c:1373:69: sparse:     expected unsigned long long [usertype] mem_addr
   drivers/firmware/qcom_scm.c:1373:69: sparse:     got restricted __le64 [usertype] ipa_base
   drivers/firmware/qcom_scm.c:1374:75: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected unsigned long [usertype] mem_sz @@     got restricted __le64 [usertype] size @@
   drivers/firmware/qcom_scm.c:1374:75: sparse:     expected unsigned long [usertype] mem_sz
   drivers/firmware/qcom_scm.c:1374:75: sparse:     got restricted __le64 [usertype] size

vim +1310 drivers/firmware/qcom_scm.c

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

Patch

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b59e3041fd62..b888068ff6f2 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -214,6 +214,8 @@  config MTK_ADSP_IPC
 
 config QCOM_SCM
 	tristate
+	select VIRT_DRIVERS
+	select GUNYAH_PLATFORM_HOOKS
 
 config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
 	bool "Qualcomm download mode enabled by default"
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 92763dce6477..bebc848cd3c5 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,94 @@  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_rpc *rm, struct gh_rm_mem_parcel *mem_parcel)
+{
+	struct qcom_scm_vmperm *new_perms;
+	u64 src, src_cpy;
+	int ret = 0, i, n;
+
+	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;
+	}
+
+	src = (1ull << QCOM_SCM_VMID_HLOS);
+
+	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 |= (1ull << mem_parcel->acl_entries[n].vmid);
+				else
+					src |= (1ull << QCOM_SCM_RM_MANAGED_VMID);
+			}
+
+			new_perms[0].vmid = QCOM_SCM_VMID_HLOS;
+
+			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_rpc *rm,
+						struct gh_rm_mem_parcel *mem_parcel)
+{
+	struct qcom_scm_vmperm new_perms;
+	u64 src = 0;
+	int ret = 0, i, n;
+
+	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++) {
+		if (mem_parcel->acl_entries[n].vmid <= QCOM_SCM_MAX_MANAGED_VMID)
+			src |= (1ull << mem_parcel->acl_entries[n].vmid);
+		else
+			src |= (1ull << 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 +1506,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;
 }