[v6,20/21] firmware: qcom_scm: Register Gunyah platform ops
Commit Message
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
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
@@ -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;
}
@@ -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);