Avoid NULL pointer dereference while using any qcom SCM calls. Add macro to easy the check at each SCM calls. Changes in v2: - Cleanup in commit-message Signed-off-by: Wasim Nazir <quic_wasimn@xxxxxxxxxxx> diff --git a/drivers/firmware/qcom/qcom_scm-legacy.c b/drivers/firmware/qcom/qcom_scm-legacy.c index 029e6d117cb8..3247145a6583 100644 --- a/drivers/firmware/qcom/qcom_scm-legacy.c +++ b/drivers/firmware/qcom/qcom_scm-legacy.c @@ -148,6 +148,9 @@ int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, __le32 *arg_buf; const __le32 *res_buf; + if (!dev) + return -EPROBE_DEFER; + cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); if (!cmd) return -ENOMEM; diff --git a/drivers/firmware/qcom/qcom_scm-smc.c b/drivers/firmware/qcom/qcom_scm-smc.c index 2b4c2826f572..4f48fabe1625 100644 --- a/drivers/firmware/qcom/qcom_scm-smc.c +++ b/drivers/firmware/qcom/qcom_scm-smc.c @@ -173,6 +173,9 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, smc.args[i + SCM_SMC_FIRST_REG_IDX] = desc->args[i]; if (unlikely(arglen > SCM_SMC_N_REG_ARGS)) { + if (!mempool) + return -EPROBE_DEFER; + args_virt = qcom_tzmem_alloc(mempool, SCM_SMC_N_EXT_ARGS * sizeof(u64), flag); diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 10986cb11ec0..72e71fc569e3 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -3,6 +3,8 @@ * Copyright (C) 2015 Linaro Ltd. */ +#define pr_fmt(fmt) "qcom_scm: %s: " fmt, __func__ + #include <linux/arm-smccc.h> #include <linux/bitfield.h> #include <linux/bits.h> @@ -145,6 +147,8 @@ static const char * const download_mode_name[] = { static struct qcom_scm *__scm; +#define SCM_NOT_INITIALIZED() (unlikely(!__scm) ? pr_err("SCM not initialized\n") : 0) + static int qcom_scm_clk_enable(void) { int ret; @@ -216,6 +220,9 @@ static DEFINE_SPINLOCK(scm_query_lock); struct qcom_tzmem_pool *qcom_scm_get_tzmem_pool(void) { + if (SCM_NOT_INITIALIZED()) + return NULL; + return __scm->mempool; } @@ -261,7 +268,7 @@ static enum qcom_scm_convention __get_convention(void) * early calls into the firmware on these SoCs so the device pointer * will be valid here to check if the compatible matches. */ - if (of_device_is_compatible(__scm ? __scm->dev->of_node : NULL, "qcom,scm-sc7180")) { + if (of_device_is_compatible(__scm->dev->of_node, "qcom,scm-sc7180")) { forced = true; goto found; } @@ -277,7 +284,7 @@ static enum qcom_scm_convention __get_convention(void) spin_lock_irqsave(&scm_query_lock, flags); if (probed_convention != qcom_scm_convention) { qcom_scm_convention = probed_convention; - pr_info("qcom_scm: convention: %s%s\n", + pr_info("convention: %s%s\n", qcom_scm_convention_names[qcom_scm_convention], forced ? " (forced)" : ""); } @@ -378,6 +385,9 @@ static int qcom_scm_set_boot_addr(void *entry, const u8 *cpu_bits) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + for_each_present_cpu(cpu) { if (cpu >= QCOM_SCM_BOOT_MAX_CPUS) return -EINVAL; @@ -387,7 +397,7 @@ static int qcom_scm_set_boot_addr(void *entry, const u8 *cpu_bits) desc.args[0] = flags; desc.args[1] = virt_to_phys(entry); - return qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); + return qcom_scm_call_atomic(__scm->dev, &desc, NULL); } static int qcom_scm_set_boot_addr_mc(void *entry, unsigned int flags) @@ -405,8 +415,11 @@ static int qcom_scm_set_boot_addr_mc(void *entry, unsigned int flags) }, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + /* Need a device for DMA of the additional arguments */ - if (!__scm || __get_convention() == SMC_CONVENTION_LEGACY) + if (__get_convention() == SMC_CONVENTION_LEGACY) return -EOPNOTSUPP; return qcom_scm_call(__scm->dev, &desc, NULL); @@ -459,7 +472,10 @@ void qcom_scm_cpu_power_down(u32 flags) .owner = ARM_SMCCC_OWNER_SIP, }; - qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); + if (SCM_NOT_INITIALIZED()) + return; + + qcom_scm_call_atomic(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down); @@ -476,6 +492,9 @@ int qcom_scm_set_remote_state(u32 state, u32 id) struct qcom_scm_res res; int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_call(__scm->dev, &desc, &res); return ret ? : res.result[0]; @@ -539,6 +558,9 @@ static void qcom_scm_set_download_mode(u32 dload_mode) { int ret = 0; + if (SCM_NOT_INITIALIZED()) + return; + if (__scm->dload_mode_addr) { ret = qcom_scm_io_rmw(__scm->dload_mode_addr, QCOM_DLOAD_MASK, FIELD_PREP(QCOM_DLOAD_MASK, dload_mode)); @@ -586,6 +608,9 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, }; struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + /* * During the scm call memory protection will be enabled for the meta * data blob, so make sure it's physically contiguous, 4K aligned and @@ -640,6 +665,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); */ void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx) { + if (SCM_NOT_INITIALIZED()) + return; + if (!ctx->ptr) return; @@ -674,6 +702,9 @@ int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) }; struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_clk_enable(); if (ret) return ret; @@ -711,6 +742,9 @@ int qcom_scm_pas_auth_and_reset(u32 peripheral) }; struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_clk_enable(); if (ret) return ret; @@ -747,6 +781,9 @@ int qcom_scm_pas_shutdown(u32 peripheral) }; struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_clk_enable(); if (ret) return ret; @@ -784,6 +821,9 @@ bool qcom_scm_pas_supported(u32 peripheral) }; struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return false; + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PIL_PAS_IS_SUPPORTED)) return false; @@ -807,7 +847,7 @@ static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) struct qcom_scm_res res; int ret; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); return ret ? : res.result[0]; } @@ -818,6 +858,9 @@ static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, if (idx != 0) return -EINVAL; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return __qcom_scm_pas_mss_reset(__scm->dev, 1); } @@ -827,6 +870,9 @@ static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev, if (idx != 0) return -EINVAL; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return __qcom_scm_pas_mss_reset(__scm->dev, 0); } @@ -847,6 +893,8 @@ int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) struct qcom_scm_res res; int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; ret = qcom_scm_call_atomic(__scm->dev, &desc, &res); if (ret >= 0) @@ -867,6 +915,9 @@ int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call_atomic(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_io_writel); @@ -879,6 +930,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_io_writel); */ bool qcom_scm_restore_sec_cfg_available(void) { + if (SCM_NOT_INITIALIZED()) + return false; + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, QCOM_SCM_MP_RESTORE_SEC_CFG); } @@ -897,6 +951,9 @@ int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) struct qcom_scm_res res; int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_call(__scm->dev, &desc, &res); return ret ? : res.result[0]; @@ -915,6 +972,9 @@ int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) struct qcom_scm_res res; int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_call(__scm->dev, &desc, &res); if (size) @@ -938,6 +998,9 @@ int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) }; int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_call(__scm->dev, &desc, NULL); /* the pg table has been initialized already, ignore the error */ @@ -959,6 +1022,9 @@ int qcom_scm_iommu_set_cp_pool_size(u32 spare, u32 size) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_cp_pool_size); @@ -981,6 +1047,9 @@ int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, }; struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_call(__scm->dev, &desc, &res); return ret ? : res.result[0]; @@ -1045,6 +1114,9 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, int ret, i, b; u64 srcvm_bits = *srcvm; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + src_sz = hweight64(srcvm_bits) * sizeof(*src); mem_to_map_sz = sizeof(*mem_to_map); dest_sz = dest_cnt * sizeof(*destvm); @@ -1102,6 +1174,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_assign_mem); */ bool qcom_scm_ocmem_lock_available(void) { + if (SCM_NOT_INITIALIZED()) + return false; + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_OCMEM, QCOM_SCM_OCMEM_LOCK_CMD); } @@ -1129,6 +1204,9 @@ int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, .arginfo = QCOM_SCM_ARGS(4), }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_ocmem_lock); @@ -1152,6 +1230,9 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) .arginfo = QCOM_SCM_ARGS(3), }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock); @@ -1164,6 +1245,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_ocmem_unlock); */ bool qcom_scm_ice_available(void) { + if (SCM_NOT_INITIALIZED()) + return false; + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, QCOM_SCM_ES_INVALIDATE_ICE_KEY) && __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, @@ -1193,6 +1277,9 @@ int qcom_scm_ice_invalidate_key(u32 index) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_ice_invalidate_key); @@ -1236,6 +1323,9 @@ int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + void *keybuf __free(qcom_tzmem) = qcom_tzmem_alloc(__scm->mempool, key_size, GFP_KERNEL); @@ -1260,10 +1350,14 @@ EXPORT_SYMBOL_GPL(qcom_scm_ice_set_key); bool qcom_scm_hdcp_available(void) { bool avail; - int ret = qcom_scm_clk_enable(); + int ret; + + if (SCM_NOT_INITIALIZED()) + return false; + ret = qcom_scm_clk_enable(); if (ret) - return ret; + return false; avail = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_HDCP_INVOKE); @@ -1305,6 +1399,9 @@ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) }; struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) return -ERANGE; @@ -1333,6 +1430,9 @@ int qcom_scm_iommu_set_pt_format(u32 sec_id, u32 ctx_num, u32 pt_fmt) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_iommu_set_pt_format); @@ -1348,6 +1448,8 @@ int qcom_scm_qsmmu500_wait_safe_toggle(bool en) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; return qcom_scm_call_atomic(__scm->dev, &desc, NULL); } @@ -1355,6 +1457,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_qsmmu500_wait_safe_toggle); bool qcom_scm_lmh_dcvsh_available(void) { + if (SCM_NOT_INITIALIZED()) + return false; + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_LMH, QCOM_SCM_LMH_LIMIT_DCVSH); } EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available); @@ -1369,6 +1474,9 @@ int qcom_scm_shm_bridge_enable(void) struct qcom_scm_res res; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, QCOM_SCM_MP_SHM_BRIDGE_ENABLE)) return -EOPNOTSUPP; @@ -1396,6 +1504,9 @@ int qcom_scm_shm_bridge_create(struct device *dev, u64 pfn_and_ns_perm_flags, struct qcom_scm_res res; int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_call(__scm->dev, &desc, &res); if (handle && !ret) @@ -1415,6 +1526,9 @@ int qcom_scm_shm_bridge_delete(struct device *dev, u64 handle) .arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL), }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_delete); @@ -1429,6 +1543,9 @@ int qcom_scm_lmh_profile_change(u32 profile_id) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_lmh_profile_change); @@ -1450,6 +1567,9 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + u32 *payload_buf __free(qcom_tzmem) = qcom_tzmem_alloc(__scm->mempool, payload_size, GFP_KERNEL); @@ -1480,6 +1600,9 @@ int qcom_scm_gpu_init_regs(u32 gpu_req) .owner = ARM_SMCCC_OWNER_SIP, }; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + return qcom_scm_call(__scm->dev, &desc, NULL); } EXPORT_SYMBOL_GPL(qcom_scm_gpu_init_regs); @@ -1554,6 +1677,9 @@ static int qcom_scm_qseecom_call(const struct qcom_scm_desc *desc, { int status; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + /* * Note: Multiple QSEECOM SCM calls should not be executed same time, * so lock things here. This needs to be extended to callback/listener @@ -1848,6 +1974,9 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx) { int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); if (ret) return ret; @@ -1861,6 +1990,9 @@ static int qcom_scm_waitq_wakeup(unsigned int wq_ctx) { int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); if (ret) return ret; @@ -1910,11 +2042,14 @@ static int set_download_mode(const char *val, const struct kernel_param *kp) bool tmp; int ret; + if (SCM_NOT_INITIALIZED()) + return -ENODEV; + ret = sysfs_match_string(download_mode_name, val); if (ret < 0) { ret = kstrtobool(val, &tmp); if (ret < 0) { - pr_err("qcom_scm: err: %d\n", ret); + pr_err("err: %d\n", ret); return ret; } @@ -1922,8 +2057,7 @@ static int set_download_mode(const char *val, const struct kernel_param *kp) } download_mode = ret; - if (__scm) - qcom_scm_set_download_mode(download_mode); + qcom_scm_set_download_mode(download_mode); return 0; } @@ -1971,13 +2105,6 @@ static int qcom_scm_probe(struct platform_device *pdev) if (IS_ERR(scm->bus_clk)) return PTR_ERR(scm->bus_clk); - scm->reset.ops = &qcom_scm_pas_reset_ops; - scm->reset.nr_resets = 1; - scm->reset.of_node = pdev->dev.of_node; - ret = devm_reset_controller_register(&pdev->dev, &scm->reset); - if (ret) - return ret; - /* vote for max clk rate for highest performance */ ret = clk_set_rate(scm->core_clk, INT_MAX); if (ret) @@ -1986,6 +2113,13 @@ static int qcom_scm_probe(struct platform_device *pdev) /* Let all above stores be available after this */ smp_store_release(&__scm, scm); + __scm->reset.ops = &qcom_scm_pas_reset_ops; + __scm->reset.nr_resets = 1; + __scm->reset.of_node = pdev->dev.of_node; + ret = devm_reset_controller_register(&pdev->dev, &__scm->reset); + if (ret) + return ret; + irq = platform_get_irq_optional(pdev, 0); if (irq < 0) { if (irq != -ENXIO) -- 2.46.1