Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'rproc-v4.17' of git://github.com/andersson/remoteproc

Pull remoteproc updates from Bjorn Andersson:

- add support for generating coredumps for remoteprocs using
devcoredump

- add the Qualcomm sysmon driver for intra-remoteproc crash handling

- a number of fixes in Qualcomm and IMX drivers

* tag 'rproc-v4.17' of git://github.com/andersson/remoteproc:
remoteproc: fix null pointer dereference on glink only platforms
soc: qcom: qmi: add CONFIG_NET dependency
remoteproc: imx_rproc: Slightly simplify code in 'imx_rproc_probe()'
remoteproc: imx_rproc: Re-use existing error handling path in 'imx_rproc_probe()'
remoteproc: imx_rproc: Fix an error handling path in 'imx_rproc_probe()'
samples: Introduce Qualcomm QMI sample client
remoteproc: qcom: Introduce sysmon
remoteproc: Pass type of shutdown to subdev remove
remoteproc: qcom: Register segments for core dump
soc: qcom: mdt-loader: Return relocation base
remoteproc: Rename "load_rsc_table" to "parse_fw"
remoteproc: Add remote processor coredump support
remoteproc: Remove null character write of shared mem

+1524 -56
+2 -2
drivers/gpu/drm/msm/adreno/a5xx_gpu.c
··· 89 89 */ 90 90 if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) { 91 91 ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID, 92 - mem_region, mem_phys, mem_size); 92 + mem_region, mem_phys, mem_size, NULL); 93 93 } else { 94 94 char newname[strlen("qcom/") + strlen(fwname) + 1]; 95 95 96 96 sprintf(newname, "qcom/%s", fwname); 97 97 98 98 ret = qcom_mdt_load(dev, fw, newname, GPU_PAS_ID, 99 - mem_region, mem_phys, mem_size); 99 + mem_region, mem_phys, mem_size, NULL); 100 100 } 101 101 if (ret) 102 102 goto out;
+1 -1
drivers/media/platform/qcom/venus/firmware.c
··· 76 76 } 77 77 78 78 ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys, 79 - mem_size); 79 + mem_size, NULL); 80 80 81 81 release_firmware(mdt); 82 82
+19
drivers/remoteproc/Kconfig
··· 6 6 select CRC32 7 7 select FW_LOADER 8 8 select VIRTIO 9 + select WANT_DEV_COREDUMP 9 10 help 10 11 Support for remote processors (such as DSP coprocessors). These 11 12 are mainly used on embedded systems. ··· 91 90 depends on QCOM_SMEM 92 91 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) 93 92 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n 93 + depends on QCOM_SYSMON || QCOM_SYSMON=n 94 94 select MFD_SYSCON 95 95 select QCOM_MDT_LOADER 96 96 select QCOM_RPROC_COMMON ··· 109 107 depends on QCOM_SMEM 110 108 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) 111 109 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n 110 + depends on QCOM_SYSMON || QCOM_SYSMON=n 112 111 select MFD_SYSCON 113 112 select QCOM_RPROC_COMMON 114 113 select QCOM_SCM ··· 117 114 Say y here to support the Qualcomm Peripherial Image Loader for the 118 115 Hexagon V5 based remote processors. 119 116 117 + config QCOM_SYSMON 118 + tristate "Qualcomm sysmon driver" 119 + depends on RPMSG 120 + depends on ARCH_QCOM 121 + depends on NET 122 + select QCOM_QMI_HELPERS 123 + help 124 + The sysmon driver implements a sysmon QMI client and a handler for 125 + the sys_mon SMD and GLINK channel, which are used for graceful 126 + shutdown, retrieving failure information and propagating information 127 + about other subsystems being shut down. 128 + 129 + Say y here if your system runs firmware on any other subsystems, e.g. 130 + modem or DSP. 131 + 120 132 config QCOM_WCNSS_PIL 121 133 tristate "Qualcomm WCNSS Peripheral Image Loader" 122 134 depends on OF && ARCH_QCOM 123 135 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) 124 136 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n 125 137 depends on QCOM_SMEM 138 + depends on QCOM_SYSMON || QCOM_SYSMON=n 126 139 select QCOM_MDT_LOADER 127 140 select QCOM_RPROC_COMMON 128 141 select QCOM_SCM
+1
drivers/remoteproc/Makefile
··· 17 17 obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o 18 18 obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o 19 19 obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o 20 + obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o 20 21 obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o 21 22 qcom_wcnss_pil-y += qcom_wcnss.o 22 23 qcom_wcnss_pil-y += qcom_wcnss_iris.o
+11 -12
drivers/remoteproc/imx_rproc.c
··· 333 333 /* set some other name then imx */ 334 334 rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, 335 335 NULL, sizeof(*priv)); 336 - if (!rproc) { 337 - ret = -ENOMEM; 338 - goto err; 339 - } 336 + if (!rproc) 337 + return -ENOMEM; 340 338 341 339 dcfg = of_device_get_match_data(dev); 342 - if (!dcfg) 343 - return -EINVAL; 340 + if (!dcfg) { 341 + ret = -EINVAL; 342 + goto err_put_rproc; 343 + } 344 344 345 345 priv = rproc->priv; 346 346 priv->rproc = rproc; ··· 359 359 priv->clk = devm_clk_get(dev, NULL); 360 360 if (IS_ERR(priv->clk)) { 361 361 dev_err(dev, "Failed to get clock\n"); 362 - rproc_free(rproc); 363 - return PTR_ERR(priv->clk); 362 + ret = PTR_ERR(priv->clk); 363 + goto err_put_rproc; 364 364 } 365 365 366 366 /* ··· 370 370 ret = clk_prepare_enable(priv->clk); 371 371 if (ret) { 372 372 dev_err(&rproc->dev, "Failed to enable clock\n"); 373 - rproc_free(rproc); 374 - return ret; 373 + goto err_put_rproc; 375 374 } 376 375 377 376 ret = rproc_add(rproc); ··· 379 380 goto err_put_clk; 380 381 } 381 382 382 - return ret; 383 + return 0; 383 384 384 385 err_put_clk: 385 386 clk_disable_unprepare(priv->clk); 386 387 err_put_rproc: 387 388 rproc_free(rproc); 388 - err: 389 + 389 390 return ret; 390 391 } 391 392
+16 -4
drivers/remoteproc/qcom_adsp_pil.c
··· 38 38 const char *firmware_name; 39 39 int pas_id; 40 40 bool has_aggre2_clk; 41 + 41 42 const char *ssr_name; 43 + const char *sysmon_name; 44 + int ssctl_id; 42 45 }; 43 46 44 47 struct qcom_adsp { ··· 78 75 struct qcom_rproc_glink glink_subdev; 79 76 struct qcom_rproc_subdev smd_subdev; 80 77 struct qcom_rproc_ssr ssr_subdev; 78 + struct qcom_sysmon *sysmon; 81 79 }; 82 80 83 81 static int adsp_load(struct rproc *rproc, const struct firmware *fw) ··· 86 82 struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; 87 83 88 84 return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id, 89 - adsp->mem_region, adsp->mem_phys, adsp->mem_size); 85 + adsp->mem_region, adsp->mem_phys, adsp->mem_size, 86 + &adsp->mem_reloc); 87 + 90 88 } 91 89 92 90 static int adsp_start(struct rproc *rproc) ··· 183 177 .start = adsp_start, 184 178 .stop = adsp_stop, 185 179 .da_to_va = adsp_da_to_va, 180 + .parse_fw = qcom_register_dump_segments, 186 181 .load = adsp_load, 187 182 }; 188 183 ··· 207 200 dev_err(adsp->dev, "fatal error received: %s\n", msg); 208 201 209 202 rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR); 210 - 211 - if (!IS_ERR(msg)) 212 - msg[0] = '\0'; 213 203 214 204 return IRQ_HANDLED; 215 205 } ··· 402 398 qcom_add_glink_subdev(rproc, &adsp->glink_subdev); 403 399 qcom_add_smd_subdev(rproc, &adsp->smd_subdev); 404 400 qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); 401 + adsp->sysmon = qcom_add_sysmon_subdev(rproc, 402 + desc->sysmon_name, 403 + desc->ssctl_id); 405 404 406 405 ret = rproc_add(rproc); 407 406 if (ret) ··· 426 419 rproc_del(adsp->rproc); 427 420 428 421 qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); 422 + qcom_remove_sysmon_subdev(adsp->sysmon); 429 423 qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); 430 424 qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); 431 425 rproc_free(adsp->rproc); ··· 440 432 .pas_id = 1, 441 433 .has_aggre2_clk = false, 442 434 .ssr_name = "lpass", 435 + .sysmon_name = "adsp", 436 + .ssctl_id = 0x14, 443 437 }; 444 438 445 439 static const struct adsp_data slpi_resource_init = { ··· 450 440 .pas_id = 12, 451 441 .has_aggre2_clk = true, 452 442 .ssr_name = "dsps", 443 + .sysmon_name = "slpi", 444 + .ssctl_id = 0x16, 453 445 }; 454 446 455 447 static const struct of_device_id adsp_of_match[] = {
+53 -3
drivers/remoteproc/qcom_common.c
··· 22 22 #include <linux/remoteproc.h> 23 23 #include <linux/rpmsg/qcom_glink.h> 24 24 #include <linux/rpmsg/qcom_smd.h> 25 + #include <linux/soc/qcom/mdt_loader.h> 25 26 26 27 #include "remoteproc_internal.h" 27 28 #include "qcom_common.h" ··· 42 41 return PTR_ERR_OR_ZERO(glink->edge); 43 42 } 44 43 45 - static void glink_subdev_remove(struct rproc_subdev *subdev) 44 + static void glink_subdev_remove(struct rproc_subdev *subdev, bool crashed) 46 45 { 47 46 struct qcom_rproc_glink *glink = to_glink_subdev(subdev); 48 47 ··· 75 74 */ 76 75 void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) 77 76 { 77 + if (!glink->node) 78 + return; 79 + 78 80 rproc_remove_subdev(rproc, &glink->subdev); 79 81 of_node_put(glink->node); 80 82 } 81 83 EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev); 84 + 85 + /** 86 + * qcom_register_dump_segments() - register segments for coredump 87 + * @rproc: remoteproc handle 88 + * @fw: firmware header 89 + * 90 + * Register all segments of the ELF in the remoteproc coredump segment list 91 + * 92 + * Return: 0 on success, negative errno on failure. 93 + */ 94 + int qcom_register_dump_segments(struct rproc *rproc, 95 + const struct firmware *fw) 96 + { 97 + const struct elf32_phdr *phdrs; 98 + const struct elf32_phdr *phdr; 99 + const struct elf32_hdr *ehdr; 100 + int ret; 101 + int i; 102 + 103 + ehdr = (struct elf32_hdr *)fw->data; 104 + phdrs = (struct elf32_phdr *)(ehdr + 1); 105 + 106 + for (i = 0; i < ehdr->e_phnum; i++) { 107 + phdr = &phdrs[i]; 108 + 109 + if (phdr->p_type != PT_LOAD) 110 + continue; 111 + 112 + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) 113 + continue; 114 + 115 + if (!phdr->p_memsz) 116 + continue; 117 + 118 + ret = rproc_coredump_add_segment(rproc, phdr->p_paddr, 119 + phdr->p_memsz); 120 + if (ret) 121 + return ret; 122 + } 123 + 124 + return 0; 125 + } 126 + EXPORT_SYMBOL_GPL(qcom_register_dump_segments); 82 127 83 128 static int smd_subdev_probe(struct rproc_subdev *subdev) 84 129 { ··· 135 88 return PTR_ERR_OR_ZERO(smd->edge); 136 89 } 137 90 138 - static void smd_subdev_remove(struct rproc_subdev *subdev) 91 + static void smd_subdev_remove(struct rproc_subdev *subdev, bool crashed) 139 92 { 140 93 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); 141 94 ··· 168 121 */ 169 122 void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) 170 123 { 124 + if (!smd->node) 125 + return; 126 + 171 127 rproc_remove_subdev(rproc, &smd->subdev); 172 128 of_node_put(smd->node); 173 129 } ··· 207 157 return 0; 208 158 } 209 159 210 - static void ssr_notify_stop(struct rproc_subdev *subdev) 160 + static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed) 211 161 { 212 162 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); 213 163
+23
drivers/remoteproc/qcom_common.h
··· 4 4 5 5 #include <linux/remoteproc.h> 6 6 #include "remoteproc_internal.h" 7 + #include <linux/soc/qcom/qmi.h> 8 + 9 + struct qcom_sysmon; 7 10 8 11 struct qcom_rproc_glink { 9 12 struct rproc_subdev subdev; ··· 33 30 void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); 34 31 void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); 35 32 33 + int qcom_register_dump_segments(struct rproc *rproc, const struct firmware *fw); 34 + 36 35 void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); 37 36 void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); 38 37 39 38 void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, 40 39 const char *ssr_name); 41 40 void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr); 41 + 42 + #if IS_ENABLED(CONFIG_QCOM_SYSMON) 43 + struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, 44 + const char *name, 45 + int ssctl_instance); 46 + void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon); 47 + #else 48 + static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, 49 + const char *name, 50 + int ssctl_instance) 51 + { 52 + return NULL; 53 + } 54 + 55 + static inline void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon) 56 + { 57 + } 58 + #endif 42 59 43 60 #endif
+3 -6
drivers/remoteproc/qcom_q6v5_pil.c
··· 168 168 169 169 struct qcom_rproc_subdev smd_subdev; 170 170 struct qcom_rproc_ssr ssr_subdev; 171 + struct qcom_sysmon *sysmon; 171 172 bool need_mem_protection; 172 173 int mpss_perm; 173 174 int mba_perm; ··· 940 939 941 940 rproc_report_crash(qproc->rproc, RPROC_WATCHDOG); 942 941 943 - if (!IS_ERR(msg)) 944 - msg[0] = '\0'; 945 - 946 942 return IRQ_HANDLED; 947 943 } 948 944 ··· 956 958 dev_err(qproc->dev, "fatal error without message\n"); 957 959 958 960 rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR); 959 - 960 - if (!IS_ERR(msg)) 961 - msg[0] = '\0'; 962 961 963 962 return IRQ_HANDLED; 964 963 } ··· 1210 1215 qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); 1211 1216 qcom_add_smd_subdev(rproc, &qproc->smd_subdev); 1212 1217 qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); 1218 + qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); 1213 1219 1214 1220 ret = rproc_add(rproc); 1215 1221 if (ret) ··· 1230 1234 1231 1235 rproc_del(qproc->rproc); 1232 1236 1237 + qcom_remove_sysmon_subdev(qproc->sysmon); 1233 1238 qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); 1234 1239 qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); 1235 1240 rproc_free(qproc->rproc);
+579
drivers/remoteproc/qcom_sysmon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) 2017, Linaro Ltd. 4 + */ 5 + #include <linux/firmware.h> 6 + #include <linux/module.h> 7 + #include <linux/notifier.h> 8 + #include <linux/slab.h> 9 + #include <linux/io.h> 10 + #include <linux/notifier.h> 11 + #include <linux/of_platform.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/remoteproc/qcom_rproc.h> 14 + #include <linux/rpmsg.h> 15 + 16 + #include "qcom_common.h" 17 + 18 + static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers); 19 + 20 + struct qcom_sysmon { 21 + struct rproc_subdev subdev; 22 + struct rproc *rproc; 23 + 24 + struct list_head node; 25 + 26 + const char *name; 27 + 28 + int ssctl_version; 29 + int ssctl_instance; 30 + 31 + struct notifier_block nb; 32 + 33 + struct device *dev; 34 + 35 + struct rpmsg_endpoint *ept; 36 + struct completion comp; 37 + struct mutex lock; 38 + 39 + bool ssr_ack; 40 + 41 + struct qmi_handle qmi; 42 + struct sockaddr_qrtr ssctl; 43 + }; 44 + 45 + static DEFINE_MUTEX(sysmon_lock); 46 + static LIST_HEAD(sysmon_list); 47 + 48 + /** 49 + * sysmon_send_event() - send notification of other remote's SSR event 50 + * @sysmon: sysmon context 51 + * @name: other remote's name 52 + */ 53 + static void sysmon_send_event(struct qcom_sysmon *sysmon, const char *name) 54 + { 55 + char req[50]; 56 + int len; 57 + int ret; 58 + 59 + len = snprintf(req, sizeof(req), "ssr:%s:before_shutdown", name); 60 + if (len >= sizeof(req)) 61 + return; 62 + 63 + mutex_lock(&sysmon->lock); 64 + reinit_completion(&sysmon->comp); 65 + sysmon->ssr_ack = false; 66 + 67 + ret = rpmsg_send(sysmon->ept, req, len); 68 + if (ret < 0) { 69 + dev_err(sysmon->dev, "failed to send sysmon event\n"); 70 + goto out_unlock; 71 + } 72 + 73 + ret = wait_for_completion_timeout(&sysmon->comp, 74 + msecs_to_jiffies(5000)); 75 + if (!ret) { 76 + dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); 77 + goto out_unlock; 78 + } 79 + 80 + if (!sysmon->ssr_ack) 81 + dev_err(sysmon->dev, "unexpected response to sysmon event\n"); 82 + 83 + out_unlock: 84 + mutex_unlock(&sysmon->lock); 85 + } 86 + 87 + /** 88 + * sysmon_request_shutdown() - request graceful shutdown of remote 89 + * @sysmon: sysmon context 90 + */ 91 + static void sysmon_request_shutdown(struct qcom_sysmon *sysmon) 92 + { 93 + char *req = "ssr:shutdown"; 94 + int ret; 95 + 96 + mutex_lock(&sysmon->lock); 97 + reinit_completion(&sysmon->comp); 98 + sysmon->ssr_ack = false; 99 + 100 + ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1); 101 + if (ret < 0) { 102 + dev_err(sysmon->dev, "send sysmon shutdown request failed\n"); 103 + goto out_unlock; 104 + } 105 + 106 + ret = wait_for_completion_timeout(&sysmon->comp, 107 + msecs_to_jiffies(5000)); 108 + if (!ret) { 109 + dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); 110 + goto out_unlock; 111 + } 112 + 113 + if (!sysmon->ssr_ack) 114 + dev_err(sysmon->dev, 115 + "unexpected response to sysmon shutdown request\n"); 116 + 117 + out_unlock: 118 + mutex_unlock(&sysmon->lock); 119 + } 120 + 121 + static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, 122 + void *priv, u32 addr) 123 + { 124 + struct qcom_sysmon *sysmon = priv; 125 + const char *ssr_ack = "ssr:ack"; 126 + const int ssr_ack_len = strlen(ssr_ack) + 1; 127 + 128 + if (!sysmon) 129 + return -EINVAL; 130 + 131 + if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len)) 132 + sysmon->ssr_ack = true; 133 + 134 + complete(&sysmon->comp); 135 + 136 + return 0; 137 + } 138 + 139 + #define SSCTL_SHUTDOWN_REQ 0x21 140 + #define SSCTL_SUBSYS_EVENT_REQ 0x23 141 + 142 + #define SSCTL_MAX_MSG_LEN 7 143 + 144 + #define SSCTL_SUBSYS_NAME_LENGTH 15 145 + 146 + enum { 147 + SSCTL_SSR_EVENT_BEFORE_POWERUP, 148 + SSCTL_SSR_EVENT_AFTER_POWERUP, 149 + SSCTL_SSR_EVENT_BEFORE_SHUTDOWN, 150 + SSCTL_SSR_EVENT_AFTER_SHUTDOWN, 151 + }; 152 + 153 + enum { 154 + SSCTL_SSR_EVENT_FORCED, 155 + SSCTL_SSR_EVENT_GRACEFUL, 156 + }; 157 + 158 + struct ssctl_shutdown_resp { 159 + struct qmi_response_type_v01 resp; 160 + }; 161 + 162 + static struct qmi_elem_info ssctl_shutdown_resp_ei[] = { 163 + { 164 + .data_type = QMI_STRUCT, 165 + .elem_len = 1, 166 + .elem_size = sizeof(struct qmi_response_type_v01), 167 + .array_type = NO_ARRAY, 168 + .tlv_type = 0x02, 169 + .offset = offsetof(struct ssctl_shutdown_resp, resp), 170 + .ei_array = qmi_response_type_v01_ei, 171 + }, 172 + {} 173 + }; 174 + 175 + struct ssctl_subsys_event_req { 176 + u8 subsys_name_len; 177 + char subsys_name[SSCTL_SUBSYS_NAME_LENGTH]; 178 + u32 event; 179 + u8 evt_driven_valid; 180 + u32 evt_driven; 181 + }; 182 + 183 + static struct qmi_elem_info ssctl_subsys_event_req_ei[] = { 184 + { 185 + .data_type = QMI_DATA_LEN, 186 + .elem_len = 1, 187 + .elem_size = sizeof(uint8_t), 188 + .array_type = NO_ARRAY, 189 + .tlv_type = 0x01, 190 + .offset = offsetof(struct ssctl_subsys_event_req, 191 + subsys_name_len), 192 + .ei_array = NULL, 193 + }, 194 + { 195 + .data_type = QMI_UNSIGNED_1_BYTE, 196 + .elem_len = SSCTL_SUBSYS_NAME_LENGTH, 197 + .elem_size = sizeof(char), 198 + .array_type = VAR_LEN_ARRAY, 199 + .tlv_type = 0x01, 200 + .offset = offsetof(struct ssctl_subsys_event_req, 201 + subsys_name), 202 + .ei_array = NULL, 203 + }, 204 + { 205 + .data_type = QMI_SIGNED_4_BYTE_ENUM, 206 + .elem_len = 1, 207 + .elem_size = sizeof(uint32_t), 208 + .array_type = NO_ARRAY, 209 + .tlv_type = 0x02, 210 + .offset = offsetof(struct ssctl_subsys_event_req, 211 + event), 212 + .ei_array = NULL, 213 + }, 214 + { 215 + .data_type = QMI_OPT_FLAG, 216 + .elem_len = 1, 217 + .elem_size = sizeof(uint8_t), 218 + .array_type = NO_ARRAY, 219 + .tlv_type = 0x10, 220 + .offset = offsetof(struct ssctl_subsys_event_req, 221 + evt_driven_valid), 222 + .ei_array = NULL, 223 + }, 224 + { 225 + .data_type = QMI_SIGNED_4_BYTE_ENUM, 226 + .elem_len = 1, 227 + .elem_size = sizeof(uint32_t), 228 + .array_type = NO_ARRAY, 229 + .tlv_type = 0x10, 230 + .offset = offsetof(struct ssctl_subsys_event_req, 231 + evt_driven), 232 + .ei_array = NULL, 233 + }, 234 + {} 235 + }; 236 + 237 + struct ssctl_subsys_event_resp { 238 + struct qmi_response_type_v01 resp; 239 + }; 240 + 241 + static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { 242 + { 243 + .data_type = QMI_STRUCT, 244 + .elem_len = 1, 245 + .elem_size = sizeof(struct qmi_response_type_v01), 246 + .array_type = NO_ARRAY, 247 + .tlv_type = 0x02, 248 + .offset = offsetof(struct ssctl_subsys_event_resp, 249 + resp), 250 + .ei_array = qmi_response_type_v01_ei, 251 + }, 252 + {} 253 + }; 254 + 255 + /** 256 + * ssctl_request_shutdown() - request shutdown via SSCTL QMI service 257 + * @sysmon: sysmon context 258 + */ 259 + static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) 260 + { 261 + struct ssctl_shutdown_resp resp; 262 + struct qmi_txn txn; 263 + int ret; 264 + 265 + ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); 266 + if (ret < 0) { 267 + dev_err(sysmon->dev, "failed to allocate QMI txn\n"); 268 + return; 269 + } 270 + 271 + ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, 272 + SSCTL_SHUTDOWN_REQ, 0, NULL, NULL); 273 + if (ret < 0) { 274 + dev_err(sysmon->dev, "failed to send shutdown request\n"); 275 + qmi_txn_cancel(&txn); 276 + return; 277 + } 278 + 279 + ret = qmi_txn_wait(&txn, 5 * HZ); 280 + if (ret < 0) 281 + dev_err(sysmon->dev, "failed receiving QMI response\n"); 282 + else if (resp.resp.result) 283 + dev_err(sysmon->dev, "shutdown request failed\n"); 284 + else 285 + dev_dbg(sysmon->dev, "shutdown request completed\n"); 286 + } 287 + 288 + /** 289 + * ssctl_send_event() - send notification of other remote's SSR event 290 + * @sysmon: sysmon context 291 + * @name: other remote's name 292 + */ 293 + static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name) 294 + { 295 + struct ssctl_subsys_event_resp resp; 296 + struct ssctl_subsys_event_req req; 297 + struct qmi_txn txn; 298 + int ret; 299 + 300 + memset(&resp, 0, sizeof(resp)); 301 + ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp); 302 + if (ret < 0) { 303 + dev_err(sysmon->dev, "failed to allocate QMI txn\n"); 304 + return; 305 + } 306 + 307 + memset(&req, 0, sizeof(req)); 308 + strlcpy(req.subsys_name, name, sizeof(req.subsys_name)); 309 + req.subsys_name_len = strlen(req.subsys_name); 310 + req.event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN; 311 + req.evt_driven_valid = true; 312 + req.evt_driven = SSCTL_SSR_EVENT_FORCED; 313 + 314 + ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, 315 + SSCTL_SUBSYS_EVENT_REQ, 40, 316 + ssctl_subsys_event_req_ei, &req); 317 + if (ret < 0) { 318 + dev_err(sysmon->dev, "failed to send shutdown request\n"); 319 + qmi_txn_cancel(&txn); 320 + return; 321 + } 322 + 323 + ret = qmi_txn_wait(&txn, 5 * HZ); 324 + if (ret < 0) 325 + dev_err(sysmon->dev, "failed receiving QMI response\n"); 326 + else if (resp.resp.result) 327 + dev_err(sysmon->dev, "ssr event send failed\n"); 328 + else 329 + dev_dbg(sysmon->dev, "ssr event send completed\n"); 330 + } 331 + 332 + /** 333 + * ssctl_new_server() - QMI callback indicating a new service 334 + * @qmi: QMI handle 335 + * @svc: service information 336 + * 337 + * Return: 0 if we're interested in this service, -EINVAL otherwise. 338 + */ 339 + static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc) 340 + { 341 + struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); 342 + 343 + switch (svc->version) { 344 + case 1: 345 + if (svc->instance != 0) 346 + return -EINVAL; 347 + if (strcmp(sysmon->name, "modem")) 348 + return -EINVAL; 349 + break; 350 + case 2: 351 + if (svc->instance != sysmon->ssctl_instance) 352 + return -EINVAL; 353 + break; 354 + default: 355 + return -EINVAL; 356 + }; 357 + 358 + sysmon->ssctl_version = svc->version; 359 + 360 + sysmon->ssctl.sq_family = AF_QIPCRTR; 361 + sysmon->ssctl.sq_node = svc->node; 362 + sysmon->ssctl.sq_port = svc->port; 363 + 364 + svc->priv = sysmon; 365 + 366 + return 0; 367 + } 368 + 369 + /** 370 + * ssctl_del_server() - QMI callback indicating that @svc is removed 371 + * @qmi: QMI handle 372 + * @svc: service information 373 + */ 374 + static void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc) 375 + { 376 + struct qcom_sysmon *sysmon = svc->priv; 377 + 378 + sysmon->ssctl_version = 0; 379 + } 380 + 381 + static const struct qmi_ops ssctl_ops = { 382 + .new_server = ssctl_new_server, 383 + .del_server = ssctl_del_server, 384 + }; 385 + 386 + static int sysmon_start(struct rproc_subdev *subdev) 387 + { 388 + return 0; 389 + } 390 + 391 + static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) 392 + { 393 + struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); 394 + 395 + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon->name); 396 + 397 + /* Don't request graceful shutdown if we've crashed */ 398 + if (crashed) 399 + return; 400 + 401 + if (sysmon->ssctl_version) 402 + ssctl_request_shutdown(sysmon); 403 + else if (sysmon->ept) 404 + sysmon_request_shutdown(sysmon); 405 + } 406 + 407 + /** 408 + * sysmon_notify() - notify sysmon target of another's SSR 409 + * @nb: notifier_block associated with sysmon instance 410 + * @event: unused 411 + * @data: SSR identifier of the remote that is going down 412 + */ 413 + static int sysmon_notify(struct notifier_block *nb, unsigned long event, 414 + void *data) 415 + { 416 + struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb); 417 + struct rproc *rproc = sysmon->rproc; 418 + const char *ssr_name = data; 419 + 420 + /* Skip non-running rprocs and the originating instance */ 421 + if (rproc->state != RPROC_RUNNING || !strcmp(data, sysmon->name)) { 422 + dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name); 423 + return NOTIFY_DONE; 424 + } 425 + 426 + /* Only SSCTL version 2 supports SSR events */ 427 + if (sysmon->ssctl_version == 2) 428 + ssctl_send_event(sysmon, ssr_name); 429 + else if (sysmon->ept) 430 + sysmon_send_event(sysmon, ssr_name); 431 + 432 + return NOTIFY_DONE; 433 + } 434 + 435 + /** 436 + * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc 437 + * @rproc: rproc context to associate the subdev with 438 + * @name: name of this subdev, to use in SSR 439 + * @ssctl_instance: instance id of the ssctl QMI service 440 + * 441 + * Return: A new qcom_sysmon object, or NULL on failure 442 + */ 443 + struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, 444 + const char *name, 445 + int ssctl_instance) 446 + { 447 + struct qcom_sysmon *sysmon; 448 + int ret; 449 + 450 + sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); 451 + if (!sysmon) 452 + return NULL; 453 + 454 + sysmon->dev = rproc->dev.parent; 455 + sysmon->rproc = rproc; 456 + 457 + sysmon->name = name; 458 + sysmon->ssctl_instance = ssctl_instance; 459 + 460 + init_completion(&sysmon->comp); 461 + mutex_init(&sysmon->lock); 462 + 463 + ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); 464 + if (ret < 0) { 465 + dev_err(sysmon->dev, "failed to initialize qmi handle\n"); 466 + kfree(sysmon); 467 + return NULL; 468 + } 469 + 470 + qmi_add_lookup(&sysmon->qmi, 43, 0, 0); 471 + 472 + rproc_add_subdev(rproc, &sysmon->subdev, sysmon_start, sysmon_stop); 473 + 474 + sysmon->nb.notifier_call = sysmon_notify; 475 + blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb); 476 + 477 + mutex_lock(&sysmon_lock); 478 + list_add(&sysmon->node, &sysmon_list); 479 + mutex_unlock(&sysmon_lock); 480 + 481 + return sysmon; 482 + } 483 + EXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev); 484 + 485 + /** 486 + * qcom_remove_sysmon_subdev() - release a qcom_sysmon 487 + * @sysmon: sysmon context, as retrieved by qcom_add_sysmon_subdev() 488 + */ 489 + void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon) 490 + { 491 + if (!sysmon) 492 + return; 493 + 494 + mutex_lock(&sysmon_lock); 495 + list_del(&sysmon->node); 496 + mutex_unlock(&sysmon_lock); 497 + 498 + blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb); 499 + 500 + rproc_remove_subdev(sysmon->rproc, &sysmon->subdev); 501 + 502 + qmi_handle_release(&sysmon->qmi); 503 + 504 + kfree(sysmon); 505 + } 506 + EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev); 507 + 508 + /** 509 + * sysmon_probe() - probe sys_mon channel 510 + * @rpdev: rpmsg device handle 511 + * 512 + * Find the sysmon context associated with the ancestor remoteproc and assign 513 + * this rpmsg device with said sysmon context. 514 + * 515 + * Return: 0 on success, negative errno on failure. 516 + */ 517 + static int sysmon_probe(struct rpmsg_device *rpdev) 518 + { 519 + struct qcom_sysmon *sysmon; 520 + struct rproc *rproc; 521 + 522 + rproc = rproc_get_by_child(&rpdev->dev); 523 + if (!rproc) { 524 + dev_err(&rpdev->dev, "sysmon device not child of rproc\n"); 525 + return -EINVAL; 526 + } 527 + 528 + mutex_lock(&sysmon_lock); 529 + list_for_each_entry(sysmon, &sysmon_list, node) { 530 + if (sysmon->rproc == rproc) 531 + goto found; 532 + } 533 + mutex_unlock(&sysmon_lock); 534 + 535 + dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n"); 536 + 537 + return -EINVAL; 538 + 539 + found: 540 + mutex_unlock(&sysmon_lock); 541 + 542 + rpdev->ept->priv = sysmon; 543 + sysmon->ept = rpdev->ept; 544 + 545 + return 0; 546 + } 547 + 548 + /** 549 + * sysmon_remove() - sys_mon channel remove handler 550 + * @rpdev: rpmsg device handle 551 + * 552 + * Disassociate the rpmsg device with the sysmon instance. 553 + */ 554 + static void sysmon_remove(struct rpmsg_device *rpdev) 555 + { 556 + struct qcom_sysmon *sysmon = rpdev->ept->priv; 557 + 558 + sysmon->ept = NULL; 559 + } 560 + 561 + static const struct rpmsg_device_id sysmon_match[] = { 562 + { "sys_mon" }, 563 + {} 564 + }; 565 + 566 + static struct rpmsg_driver sysmon_driver = { 567 + .probe = sysmon_probe, 568 + .remove = sysmon_remove, 569 + .callback = sysmon_callback, 570 + .id_table = sysmon_match, 571 + .drv = { 572 + .name = "qcom_sysmon", 573 + }, 574 + }; 575 + 576 + module_rpmsg_driver(sysmon_driver); 577 + 578 + MODULE_DESCRIPTION("Qualcomm sysmon driver"); 579 + MODULE_LICENSE("GPL v2");
+7 -4
drivers/remoteproc/qcom_wcnss.c
··· 40 40 #define WCNSS_CRASH_REASON_SMEM 422 41 41 #define WCNSS_FIRMWARE_NAME "wcnss.mdt" 42 42 #define WCNSS_PAS_ID 6 43 + #define WCNSS_SSCTL_ID 0x13 43 44 44 45 #define WCNSS_SPARE_NVBIN_DLND BIT(25) 45 46 ··· 99 98 size_t mem_size; 100 99 101 100 struct qcom_rproc_subdev smd_subdev; 101 + struct qcom_sysmon *sysmon; 102 102 }; 103 103 104 104 static const struct wcnss_data riva_data = { ··· 155 153 struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; 156 154 157 155 return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID, 158 - wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size); 156 + wcnss->mem_region, wcnss->mem_phys, 157 + wcnss->mem_size, &wcnss->mem_reloc); 159 158 } 160 159 161 160 static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss) ··· 311 308 .start = wcnss_start, 312 309 .stop = wcnss_stop, 313 310 .da_to_va = wcnss_da_to_va, 311 + .parse_fw = qcom_register_dump_segments, 314 312 .load = wcnss_load, 315 313 }; 316 314 ··· 335 331 dev_err(wcnss->dev, "fatal error received: %s\n", msg); 336 332 337 333 rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR); 338 - 339 - if (!IS_ERR(msg)) 340 - msg[0] = '\0'; 341 334 342 335 return IRQ_HANDLED; 343 336 } ··· 552 551 } 553 552 554 553 qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); 554 + wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); 555 555 556 556 ret = rproc_add(rproc); 557 557 if (ret) ··· 575 573 qcom_smem_state_put(wcnss->state); 576 574 rproc_del(wcnss->rproc); 577 575 576 + qcom_remove_sysmon_subdev(wcnss->sysmon); 578 577 qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev); 579 578 rproc_free(wcnss->rproc); 580 579
+140 -12
drivers/remoteproc/remoteproc_core.c
··· 33 33 #include <linux/firmware.h> 34 34 #include <linux/string.h> 35 35 #include <linux/debugfs.h> 36 + #include <linux/devcoredump.h> 36 37 #include <linux/remoteproc.h> 37 38 #include <linux/iommu.h> 38 39 #include <linux/idr.h> ··· 308 307 return rproc_add_virtio_dev(rvdev, rvdev->id); 309 308 } 310 309 311 - static void rproc_vdev_do_remove(struct rproc_subdev *subdev) 310 + static void rproc_vdev_do_remove(struct rproc_subdev *subdev, bool crashed) 312 311 { 313 312 struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 314 313 ··· 789 788 790 789 unroll_registration: 791 790 list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) 792 - subdev->remove(subdev); 791 + subdev->remove(subdev, true); 793 792 794 793 return ret; 795 794 } 796 795 797 - static void rproc_remove_subdevices(struct rproc *rproc) 796 + static void rproc_remove_subdevices(struct rproc *rproc, bool crashed) 798 797 { 799 798 struct rproc_subdev *subdev; 800 799 801 800 list_for_each_entry_reverse(subdev, &rproc->subdevs, node) 802 - subdev->remove(subdev); 801 + subdev->remove(subdev, crashed); 802 + } 803 + 804 + /** 805 + * rproc_coredump_cleanup() - clean up dump_segments list 806 + * @rproc: the remote processor handle 807 + */ 808 + static void rproc_coredump_cleanup(struct rproc *rproc) 809 + { 810 + struct rproc_dump_segment *entry, *tmp; 811 + 812 + list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) { 813 + list_del(&entry->node); 814 + kfree(entry); 815 + } 803 816 } 804 817 805 818 /** ··· 863 848 /* clean up remote vdev entries */ 864 849 list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) 865 850 kref_put(&rvdev->refcount, rproc_vdev_release); 851 + 852 + rproc_coredump_cleanup(rproc); 866 853 } 867 854 868 855 static int rproc_start(struct rproc *rproc, const struct firmware *fw) ··· 944 927 945 928 rproc->bootaddr = rproc_get_boot_addr(rproc, fw); 946 929 947 - /* load resource table */ 948 - ret = rproc_load_rsc_table(rproc, fw); 930 + /* Load resource table, core dump segment list etc from the firmware */ 931 + ret = rproc_parse_fw(rproc, fw); 949 932 if (ret) 950 933 goto disable_iommu; 951 934 ··· 1009 992 return ret; 1010 993 } 1011 994 1012 - static int rproc_stop(struct rproc *rproc) 995 + static int rproc_stop(struct rproc *rproc, bool crashed) 1013 996 { 1014 997 struct device *dev = &rproc->dev; 1015 998 int ret; 1016 999 1017 1000 /* remove any subdevices for the remote processor */ 1018 - rproc_remove_subdevices(rproc); 1001 + rproc_remove_subdevices(rproc, crashed); 1019 1002 1020 1003 /* the installed resource table is no longer accessible */ 1021 1004 rproc->table_ptr = rproc->cached_table; ··· 1032 1015 dev_info(dev, "stopped remote processor %s\n", rproc->name); 1033 1016 1034 1017 return 0; 1018 + } 1019 + 1020 + /** 1021 + * rproc_coredump_add_segment() - add segment of device memory to coredump 1022 + * @rproc: handle of a remote processor 1023 + * @da: device address 1024 + * @size: size of segment 1025 + * 1026 + * Add device memory to the list of segments to be included in a coredump for 1027 + * the remoteproc. 1028 + * 1029 + * Return: 0 on success, negative errno on error. 1030 + */ 1031 + int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size) 1032 + { 1033 + struct rproc_dump_segment *segment; 1034 + 1035 + segment = kzalloc(sizeof(*segment), GFP_KERNEL); 1036 + if (!segment) 1037 + return -ENOMEM; 1038 + 1039 + segment->da = da; 1040 + segment->size = size; 1041 + 1042 + list_add_tail(&segment->node, &rproc->dump_segments); 1043 + 1044 + return 0; 1045 + } 1046 + EXPORT_SYMBOL(rproc_coredump_add_segment); 1047 + 1048 + /** 1049 + * rproc_coredump() - perform coredump 1050 + * @rproc: rproc handle 1051 + * 1052 + * This function will generate an ELF header for the registered segments 1053 + * and create a devcoredump device associated with rproc. 1054 + */ 1055 + static void rproc_coredump(struct rproc *rproc) 1056 + { 1057 + struct rproc_dump_segment *segment; 1058 + struct elf32_phdr *phdr; 1059 + struct elf32_hdr *ehdr; 1060 + size_t data_size; 1061 + size_t offset; 1062 + void *data; 1063 + void *ptr; 1064 + int phnum = 0; 1065 + 1066 + if (list_empty(&rproc->dump_segments)) 1067 + return; 1068 + 1069 + data_size = sizeof(*ehdr); 1070 + list_for_each_entry(segment, &rproc->dump_segments, node) { 1071 + data_size += sizeof(*phdr) + segment->size; 1072 + 1073 + phnum++; 1074 + } 1075 + 1076 + data = vmalloc(data_size); 1077 + if (!data) 1078 + return; 1079 + 1080 + ehdr = data; 1081 + 1082 + memset(ehdr, 0, sizeof(*ehdr)); 1083 + memcpy(ehdr->e_ident, ELFMAG, SELFMAG); 1084 + ehdr->e_ident[EI_CLASS] = ELFCLASS32; 1085 + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; 1086 + ehdr->e_ident[EI_VERSION] = EV_CURRENT; 1087 + ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; 1088 + ehdr->e_type = ET_CORE; 1089 + ehdr->e_machine = EM_NONE; 1090 + ehdr->e_version = EV_CURRENT; 1091 + ehdr->e_entry = rproc->bootaddr; 1092 + ehdr->e_phoff = sizeof(*ehdr); 1093 + ehdr->e_ehsize = sizeof(*ehdr); 1094 + ehdr->e_phentsize = sizeof(*phdr); 1095 + ehdr->e_phnum = phnum; 1096 + 1097 + phdr = data + ehdr->e_phoff; 1098 + offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum; 1099 + list_for_each_entry(segment, &rproc->dump_segments, node) { 1100 + memset(phdr, 0, sizeof(*phdr)); 1101 + phdr->p_type = PT_LOAD; 1102 + phdr->p_offset = offset; 1103 + phdr->p_vaddr = segment->da; 1104 + phdr->p_paddr = segment->da; 1105 + phdr->p_filesz = segment->size; 1106 + phdr->p_memsz = segment->size; 1107 + phdr->p_flags = PF_R | PF_W | PF_X; 1108 + phdr->p_align = 0; 1109 + 1110 + ptr = rproc_da_to_va(rproc, segment->da, segment->size); 1111 + if (!ptr) { 1112 + dev_err(&rproc->dev, 1113 + "invalid coredump segment (%pad, %zu)\n", 1114 + &segment->da, segment->size); 1115 + memset(data + offset, 0xff, segment->size); 1116 + } else { 1117 + memcpy(data + offset, ptr, segment->size); 1118 + } 1119 + 1120 + offset += phdr->p_filesz; 1121 + phdr++; 1122 + } 1123 + 1124 + dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); 1035 1125 } 1036 1126 1037 1127 /** ··· 1163 1039 if (ret) 1164 1040 return ret; 1165 1041 1166 - ret = rproc_stop(rproc); 1042 + ret = rproc_stop(rproc, false); 1167 1043 if (ret) 1168 1044 goto unlock_mutex; 1045 + 1046 + /* generate coredump */ 1047 + rproc_coredump(rproc); 1169 1048 1170 1049 /* load firmware */ 1171 1050 ret = request_firmware(&firmware_p, rproc->firmware, dev); ··· 1316 1189 if (!atomic_dec_and_test(&rproc->power)) 1317 1190 goto out; 1318 1191 1319 - ret = rproc_stop(rproc); 1192 + ret = rproc_stop(rproc, true); 1320 1193 if (ret) { 1321 1194 atomic_inc(&rproc->power); 1322 1195 goto out; ··· 1555 1428 /* Default to ELF loader if no load function is specified */ 1556 1429 if (!rproc->ops->load) { 1557 1430 rproc->ops->load = rproc_elf_load_segments; 1558 - rproc->ops->load_rsc_table = rproc_elf_load_rsc_table; 1431 + rproc->ops->parse_fw = rproc_elf_load_rsc_table; 1559 1432 rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; 1560 1433 rproc->ops->sanity_check = rproc_elf_sanity_check; 1561 1434 rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; ··· 1570 1443 INIT_LIST_HEAD(&rproc->traces); 1571 1444 INIT_LIST_HEAD(&rproc->rvdevs); 1572 1445 INIT_LIST_HEAD(&rproc->subdevs); 1446 + INIT_LIST_HEAD(&rproc->dump_segments); 1573 1447 1574 1448 INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); 1575 1449 ··· 1663 1535 void rproc_add_subdev(struct rproc *rproc, 1664 1536 struct rproc_subdev *subdev, 1665 1537 int (*probe)(struct rproc_subdev *subdev), 1666 - void (*remove)(struct rproc_subdev *subdev)) 1538 + void (*remove)(struct rproc_subdev *subdev, bool crashed)) 1667 1539 { 1668 1540 subdev->probe = probe; 1669 1541 subdev->remove = remove;
+3 -4
drivers/remoteproc/remoteproc_internal.h
··· 88 88 return -EINVAL; 89 89 } 90 90 91 - static inline int rproc_load_rsc_table(struct rproc *rproc, 92 - const struct firmware *fw) 91 + static inline int rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) 93 92 { 94 - if (rproc->ops->load_rsc_table) 95 - return rproc->ops->load_rsc_table(rproc, fw); 93 + if (rproc->ops->parse_fw) 94 + return rproc->ops->parse_fw(rproc, fw); 96 95 97 96 return 0; 98 97 }
+1 -1
drivers/soc/qcom/Kconfig
··· 37 37 38 38 config QCOM_QMI_HELPERS 39 39 tristate 40 - depends on ARCH_QCOM 40 + depends on ARCH_QCOM && NET 41 41 help 42 42 Helper library for handling QMI encoded messages. QMI encoded 43 43 messages are used in communication between the majority of QRTR
+6 -1
drivers/soc/qcom/mdt_loader.c
··· 83 83 * @mem_region: allocated memory region to load firmware into 84 84 * @mem_phys: physical address of allocated memory region 85 85 * @mem_size: size of the allocated memory region 86 + * @reloc_base: adjusted physical address after relocation 86 87 * 87 88 * Returns 0 on success, negative errno otherwise. 88 89 */ 89 90 int qcom_mdt_load(struct device *dev, const struct firmware *fw, 90 91 const char *firmware, int pas_id, void *mem_region, 91 - phys_addr_t mem_phys, size_t mem_size) 92 + phys_addr_t mem_phys, size_t mem_size, 93 + phys_addr_t *reloc_base) 92 94 { 93 95 const struct elf32_phdr *phdrs; 94 96 const struct elf32_phdr *phdr; ··· 193 191 if (phdr->p_memsz > phdr->p_filesz) 194 192 memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); 195 193 } 194 + 195 + if (reloc_base) 196 + *reloc_base = mem_reloc; 196 197 197 198 out: 198 199 kfree(fw_name);
+23 -4
include/linux/remoteproc.h
··· 344 344 int (*stop)(struct rproc *rproc); 345 345 void (*kick)(struct rproc *rproc, int vqid); 346 346 void * (*da_to_va)(struct rproc *rproc, u64 da, int len); 347 - int (*load_rsc_table)(struct rproc *rproc, const struct firmware *fw); 347 + int (*parse_fw)(struct rproc *rproc, const struct firmware *fw); 348 348 struct resource_table *(*find_loaded_rsc_table)( 349 349 struct rproc *rproc, const struct firmware *fw); 350 350 int (*load)(struct rproc *rproc, const struct firmware *fw); ··· 395 395 }; 396 396 397 397 /** 398 + * struct rproc_dump_segment - segment info from ELF header 399 + * @node: list node related to the rproc segment list 400 + * @da: device address of the segment 401 + * @size: size of the segment 402 + */ 403 + struct rproc_dump_segment { 404 + struct list_head node; 405 + 406 + dma_addr_t da; 407 + size_t size; 408 + 409 + loff_t offset; 410 + }; 411 + 412 + /** 398 413 * struct rproc - represents a physical remote processor device 399 414 * @node: list node of this rproc object 400 415 * @domain: iommu domain ··· 439 424 * @cached_table: copy of the resource table 440 425 * @table_sz: size of @cached_table 441 426 * @has_iommu: flag to indicate if remote processor is behind an MMU 427 + * @dump_segments: list of segments in the firmware 442 428 */ 443 429 struct rproc { 444 430 struct list_head node; ··· 471 455 size_t table_sz; 472 456 bool has_iommu; 473 457 bool auto_boot; 458 + struct list_head dump_segments; 474 459 }; 475 460 476 461 /** 477 462 * struct rproc_subdev - subdevice tied to a remoteproc 478 463 * @node: list node related to the rproc subdevs list 479 464 * @probe: probe function, called as the rproc is started 480 - * @remove: remove function, called as the rproc is stopped 465 + * @remove: remove function, called as the rproc is being stopped, the @crashed 466 + * parameter indicates if this originates from the a recovery 481 467 */ 482 468 struct rproc_subdev { 483 469 struct list_head node; 484 470 485 471 int (*probe)(struct rproc_subdev *subdev); 486 - void (*remove)(struct rproc_subdev *subdev); 472 + void (*remove)(struct rproc_subdev *subdev, bool crashed); 487 473 }; 488 474 489 475 /* we currently support only two vrings per rvdev */ ··· 552 534 int rproc_boot(struct rproc *rproc); 553 535 void rproc_shutdown(struct rproc *rproc); 554 536 void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); 537 + int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size); 555 538 556 539 static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) 557 540 { ··· 569 550 void rproc_add_subdev(struct rproc *rproc, 570 551 struct rproc_subdev *subdev, 571 552 int (*probe)(struct rproc_subdev *subdev), 572 - void (*remove)(struct rproc_subdev *subdev)); 553 + void (*remove)(struct rproc_subdev *subdev, bool graceful)); 573 554 574 555 void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev); 575 556
+2 -1
include/linux/soc/qcom/mdt_loader.h
··· 14 14 ssize_t qcom_mdt_get_size(const struct firmware *fw); 15 15 int qcom_mdt_load(struct device *dev, const struct firmware *fw, 16 16 const char *fw_name, int pas_id, void *mem_region, 17 - phys_addr_t mem_phys, size_t mem_size); 17 + phys_addr_t mem_phys, size_t mem_size, 18 + phys_addr_t *reloc_base); 18 19 19 20 #endif
+10
samples/Kconfig
··· 62 62 Build an example of how to dynamically add the hello 63 63 command to the kdb shell. 64 64 65 + config SAMPLE_QMI_CLIENT 66 + tristate "Build qmi client sample -- loadable modules only" 67 + depends on m 68 + depends on ARCH_QCOM 69 + depends on NET 70 + select QCOM_QMI_HELPERS 71 + help 72 + Build an QMI client sample driver, which demonstrates how to 73 + communicate with a remote QRTR service, using QMI encoded messages. 74 + 65 75 config SAMPLE_RPMSG_CLIENT 66 76 tristate "Build rpmsg client sample -- loadable modules only" 67 77 depends on RPMSG && m
+1 -1
samples/Makefile
··· 3 3 obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ 4 4 hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ 5 5 configfs/ connector/ v4l/ trace_printk/ \ 6 - vfio-mdev/ statx/ 6 + vfio-mdev/ statx/ qmi/
+1
samples/qmi/Makefile
··· 1 + obj-$(CONFIG_SAMPLE_QMI_CLIENT) += qmi_sample_client.o
+622
samples/qmi/qmi_sample_client.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Sample in-kernel QMI client driver 4 + * 5 + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. 6 + * Copyright (C) 2017 Linaro Ltd. 7 + */ 8 + #include <linux/kernel.h> 9 + #include <linux/module.h> 10 + #include <linux/debugfs.h> 11 + #include <linux/device.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/qrtr.h> 14 + #include <linux/net.h> 15 + #include <linux/completion.h> 16 + #include <linux/idr.h> 17 + #include <linux/string.h> 18 + #include <net/sock.h> 19 + #include <linux/soc/qcom/qmi.h> 20 + 21 + #define PING_REQ1_TLV_TYPE 0x1 22 + #define PING_RESP1_TLV_TYPE 0x2 23 + #define PING_OPT1_TLV_TYPE 0x10 24 + #define PING_OPT2_TLV_TYPE 0x11 25 + 26 + #define DATA_REQ1_TLV_TYPE 0x1 27 + #define DATA_RESP1_TLV_TYPE 0x2 28 + #define DATA_OPT1_TLV_TYPE 0x10 29 + #define DATA_OPT2_TLV_TYPE 0x11 30 + 31 + #define TEST_MED_DATA_SIZE_V01 8192 32 + #define TEST_MAX_NAME_SIZE_V01 255 33 + 34 + #define TEST_PING_REQ_MSG_ID_V01 0x20 35 + #define TEST_DATA_REQ_MSG_ID_V01 0x21 36 + 37 + #define TEST_PING_REQ_MAX_MSG_LEN_V01 266 38 + #define TEST_DATA_REQ_MAX_MSG_LEN_V01 8456 39 + 40 + struct test_name_type_v01 { 41 + u32 name_len; 42 + char name[TEST_MAX_NAME_SIZE_V01]; 43 + }; 44 + 45 + static struct qmi_elem_info test_name_type_v01_ei[] = { 46 + { 47 + .data_type = QMI_DATA_LEN, 48 + .elem_len = 1, 49 + .elem_size = sizeof(u8), 50 + .array_type = NO_ARRAY, 51 + .tlv_type = QMI_COMMON_TLV_TYPE, 52 + .offset = offsetof(struct test_name_type_v01, 53 + name_len), 54 + }, 55 + { 56 + .data_type = QMI_UNSIGNED_1_BYTE, 57 + .elem_len = TEST_MAX_NAME_SIZE_V01, 58 + .elem_size = sizeof(char), 59 + .array_type = VAR_LEN_ARRAY, 60 + .tlv_type = QMI_COMMON_TLV_TYPE, 61 + .offset = offsetof(struct test_name_type_v01, 62 + name), 63 + }, 64 + {} 65 + }; 66 + 67 + struct test_ping_req_msg_v01 { 68 + char ping[4]; 69 + 70 + u8 client_name_valid; 71 + struct test_name_type_v01 client_name; 72 + }; 73 + 74 + static struct qmi_elem_info test_ping_req_msg_v01_ei[] = { 75 + { 76 + .data_type = QMI_UNSIGNED_1_BYTE, 77 + .elem_len = 4, 78 + .elem_size = sizeof(char), 79 + .array_type = STATIC_ARRAY, 80 + .tlv_type = PING_REQ1_TLV_TYPE, 81 + .offset = offsetof(struct test_ping_req_msg_v01, 82 + ping), 83 + }, 84 + { 85 + .data_type = QMI_OPT_FLAG, 86 + .elem_len = 1, 87 + .elem_size = sizeof(u8), 88 + .array_type = NO_ARRAY, 89 + .tlv_type = PING_OPT1_TLV_TYPE, 90 + .offset = offsetof(struct test_ping_req_msg_v01, 91 + client_name_valid), 92 + }, 93 + { 94 + .data_type = QMI_STRUCT, 95 + .elem_len = 1, 96 + .elem_size = sizeof(struct test_name_type_v01), 97 + .array_type = NO_ARRAY, 98 + .tlv_type = PING_OPT1_TLV_TYPE, 99 + .offset = offsetof(struct test_ping_req_msg_v01, 100 + client_name), 101 + .ei_array = test_name_type_v01_ei, 102 + }, 103 + {} 104 + }; 105 + 106 + struct test_ping_resp_msg_v01 { 107 + struct qmi_response_type_v01 resp; 108 + 109 + u8 pong_valid; 110 + char pong[4]; 111 + 112 + u8 service_name_valid; 113 + struct test_name_type_v01 service_name; 114 + }; 115 + 116 + static struct qmi_elem_info test_ping_resp_msg_v01_ei[] = { 117 + { 118 + .data_type = QMI_STRUCT, 119 + .elem_len = 1, 120 + .elem_size = sizeof(struct qmi_response_type_v01), 121 + .array_type = NO_ARRAY, 122 + .tlv_type = PING_RESP1_TLV_TYPE, 123 + .offset = offsetof(struct test_ping_resp_msg_v01, 124 + resp), 125 + .ei_array = qmi_response_type_v01_ei, 126 + }, 127 + { 128 + .data_type = QMI_OPT_FLAG, 129 + .elem_len = 1, 130 + .elem_size = sizeof(u8), 131 + .array_type = NO_ARRAY, 132 + .tlv_type = PING_OPT1_TLV_TYPE, 133 + .offset = offsetof(struct test_ping_resp_msg_v01, 134 + pong_valid), 135 + }, 136 + { 137 + .data_type = QMI_UNSIGNED_1_BYTE, 138 + .elem_len = 4, 139 + .elem_size = sizeof(char), 140 + .array_type = STATIC_ARRAY, 141 + .tlv_type = PING_OPT1_TLV_TYPE, 142 + .offset = offsetof(struct test_ping_resp_msg_v01, 143 + pong), 144 + }, 145 + { 146 + .data_type = QMI_OPT_FLAG, 147 + .elem_len = 1, 148 + .elem_size = sizeof(u8), 149 + .array_type = NO_ARRAY, 150 + .tlv_type = PING_OPT2_TLV_TYPE, 151 + .offset = offsetof(struct test_ping_resp_msg_v01, 152 + service_name_valid), 153 + }, 154 + { 155 + .data_type = QMI_STRUCT, 156 + .elem_len = 1, 157 + .elem_size = sizeof(struct test_name_type_v01), 158 + .array_type = NO_ARRAY, 159 + .tlv_type = PING_OPT2_TLV_TYPE, 160 + .offset = offsetof(struct test_ping_resp_msg_v01, 161 + service_name), 162 + .ei_array = test_name_type_v01_ei, 163 + }, 164 + {} 165 + }; 166 + 167 + struct test_data_req_msg_v01 { 168 + u32 data_len; 169 + u8 data[TEST_MED_DATA_SIZE_V01]; 170 + 171 + u8 client_name_valid; 172 + struct test_name_type_v01 client_name; 173 + }; 174 + 175 + static struct qmi_elem_info test_data_req_msg_v01_ei[] = { 176 + { 177 + .data_type = QMI_DATA_LEN, 178 + .elem_len = 1, 179 + .elem_size = sizeof(u32), 180 + .array_type = NO_ARRAY, 181 + .tlv_type = DATA_REQ1_TLV_TYPE, 182 + .offset = offsetof(struct test_data_req_msg_v01, 183 + data_len), 184 + }, 185 + { 186 + .data_type = QMI_UNSIGNED_1_BYTE, 187 + .elem_len = TEST_MED_DATA_SIZE_V01, 188 + .elem_size = sizeof(u8), 189 + .array_type = VAR_LEN_ARRAY, 190 + .tlv_type = DATA_REQ1_TLV_TYPE, 191 + .offset = offsetof(struct test_data_req_msg_v01, 192 + data), 193 + }, 194 + { 195 + .data_type = QMI_OPT_FLAG, 196 + .elem_len = 1, 197 + .elem_size = sizeof(u8), 198 + .array_type = NO_ARRAY, 199 + .tlv_type = DATA_OPT1_TLV_TYPE, 200 + .offset = offsetof(struct test_data_req_msg_v01, 201 + client_name_valid), 202 + }, 203 + { 204 + .data_type = QMI_STRUCT, 205 + .elem_len = 1, 206 + .elem_size = sizeof(struct test_name_type_v01), 207 + .array_type = NO_ARRAY, 208 + .tlv_type = DATA_OPT1_TLV_TYPE, 209 + .offset = offsetof(struct test_data_req_msg_v01, 210 + client_name), 211 + .ei_array = test_name_type_v01_ei, 212 + }, 213 + {} 214 + }; 215 + 216 + struct test_data_resp_msg_v01 { 217 + struct qmi_response_type_v01 resp; 218 + 219 + u8 data_valid; 220 + u32 data_len; 221 + u8 data[TEST_MED_DATA_SIZE_V01]; 222 + 223 + u8 service_name_valid; 224 + struct test_name_type_v01 service_name; 225 + }; 226 + 227 + static struct qmi_elem_info test_data_resp_msg_v01_ei[] = { 228 + { 229 + .data_type = QMI_STRUCT, 230 + .elem_len = 1, 231 + .elem_size = sizeof(struct qmi_response_type_v01), 232 + .array_type = NO_ARRAY, 233 + .tlv_type = DATA_RESP1_TLV_TYPE, 234 + .offset = offsetof(struct test_data_resp_msg_v01, 235 + resp), 236 + .ei_array = qmi_response_type_v01_ei, 237 + }, 238 + { 239 + .data_type = QMI_OPT_FLAG, 240 + .elem_len = 1, 241 + .elem_size = sizeof(u8), 242 + .array_type = NO_ARRAY, 243 + .tlv_type = DATA_OPT1_TLV_TYPE, 244 + .offset = offsetof(struct test_data_resp_msg_v01, 245 + data_valid), 246 + }, 247 + { 248 + .data_type = QMI_DATA_LEN, 249 + .elem_len = 1, 250 + .elem_size = sizeof(u32), 251 + .array_type = NO_ARRAY, 252 + .tlv_type = DATA_OPT1_TLV_TYPE, 253 + .offset = offsetof(struct test_data_resp_msg_v01, 254 + data_len), 255 + }, 256 + { 257 + .data_type = QMI_UNSIGNED_1_BYTE, 258 + .elem_len = TEST_MED_DATA_SIZE_V01, 259 + .elem_size = sizeof(u8), 260 + .array_type = VAR_LEN_ARRAY, 261 + .tlv_type = DATA_OPT1_TLV_TYPE, 262 + .offset = offsetof(struct test_data_resp_msg_v01, 263 + data), 264 + }, 265 + { 266 + .data_type = QMI_OPT_FLAG, 267 + .elem_len = 1, 268 + .elem_size = sizeof(u8), 269 + .array_type = NO_ARRAY, 270 + .tlv_type = DATA_OPT2_TLV_TYPE, 271 + .offset = offsetof(struct test_data_resp_msg_v01, 272 + service_name_valid), 273 + }, 274 + { 275 + .data_type = QMI_STRUCT, 276 + .elem_len = 1, 277 + .elem_size = sizeof(struct test_name_type_v01), 278 + .array_type = NO_ARRAY, 279 + .tlv_type = DATA_OPT2_TLV_TYPE, 280 + .offset = offsetof(struct test_data_resp_msg_v01, 281 + service_name), 282 + .ei_array = test_name_type_v01_ei, 283 + }, 284 + {} 285 + }; 286 + 287 + /* 288 + * ping_write() - ping_pong debugfs file write handler 289 + * @file: debugfs file context 290 + * @user_buf: reference to the user data (ignored) 291 + * @count: number of bytes in @user_buf 292 + * @ppos: offset in @file to write 293 + * 294 + * This function allows user space to send out a ping_pong QMI encoded message 295 + * to the associated remote test service and will return with the result of the 296 + * transaction. It serves as an example of how to provide a custom response 297 + * handler. 298 + * 299 + * Return: @count, or negative errno on failure. 300 + */ 301 + static ssize_t ping_write(struct file *file, const char __user *user_buf, 302 + size_t count, loff_t *ppos) 303 + { 304 + struct qmi_handle *qmi = file->private_data; 305 + struct test_ping_req_msg_v01 req = {}; 306 + struct qmi_txn txn; 307 + int ret; 308 + 309 + memcpy(req.ping, "ping", sizeof(req.ping)); 310 + 311 + ret = qmi_txn_init(qmi, &txn, NULL, NULL); 312 + if (ret < 0) 313 + return ret; 314 + 315 + ret = qmi_send_request(qmi, NULL, &txn, 316 + TEST_PING_REQ_MSG_ID_V01, 317 + TEST_PING_REQ_MAX_MSG_LEN_V01, 318 + test_ping_req_msg_v01_ei, &req); 319 + if (ret < 0) { 320 + qmi_txn_cancel(&txn); 321 + return ret; 322 + } 323 + 324 + ret = qmi_txn_wait(&txn, 5 * HZ); 325 + if (ret < 0) 326 + count = ret; 327 + 328 + return count; 329 + } 330 + 331 + static const struct file_operations ping_fops = { 332 + .open = simple_open, 333 + .write = ping_write, 334 + }; 335 + 336 + static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, 337 + struct qmi_txn *txn, const void *data) 338 + { 339 + const struct test_ping_resp_msg_v01 *resp = data; 340 + 341 + if (!txn) { 342 + pr_err("spurious ping response\n"); 343 + return; 344 + } 345 + 346 + if (resp->resp.result == QMI_RESULT_FAILURE_V01) 347 + txn->result = -ENXIO; 348 + else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4)) 349 + txn->result = -EINVAL; 350 + 351 + complete(&txn->completion); 352 + } 353 + 354 + /* 355 + * data_write() - data debugfs file write handler 356 + * @file: debugfs file context 357 + * @user_buf: reference to the user data 358 + * @count: number of bytes in @user_buf 359 + * @ppos: offset in @file to write 360 + * 361 + * This function allows user space to send out a data QMI encoded message to 362 + * the associated remote test service and will return with the result of the 363 + * transaction. It serves as an example of how to have the QMI helpers decode a 364 + * transaction response into a provided object automatically. 365 + * 366 + * Return: @count, or negative errno on failure. 367 + */ 368 + static ssize_t data_write(struct file *file, const char __user *user_buf, 369 + size_t count, loff_t *ppos) 370 + 371 + { 372 + struct qmi_handle *qmi = file->private_data; 373 + struct test_data_resp_msg_v01 *resp; 374 + struct test_data_req_msg_v01 *req; 375 + struct qmi_txn txn; 376 + int ret; 377 + 378 + req = kzalloc(sizeof(*req), GFP_KERNEL); 379 + if (!req) 380 + return -ENOMEM; 381 + 382 + resp = kzalloc(sizeof(*resp), GFP_KERNEL); 383 + if (!resp) { 384 + kfree(req); 385 + return -ENOMEM; 386 + } 387 + 388 + req->data_len = min_t(size_t, sizeof(req->data), count); 389 + if (copy_from_user(req->data, user_buf, req->data_len)) { 390 + ret = -EFAULT; 391 + goto out; 392 + } 393 + 394 + ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp); 395 + if (ret < 0) 396 + goto out; 397 + 398 + ret = qmi_send_request(qmi, NULL, &txn, 399 + TEST_DATA_REQ_MSG_ID_V01, 400 + TEST_DATA_REQ_MAX_MSG_LEN_V01, 401 + test_data_req_msg_v01_ei, req); 402 + if (ret < 0) { 403 + qmi_txn_cancel(&txn); 404 + goto out; 405 + } 406 + 407 + ret = qmi_txn_wait(&txn, 5 * HZ); 408 + if (ret < 0) { 409 + goto out; 410 + } else if (!resp->data_valid || 411 + resp->data_len != req->data_len || 412 + memcmp(resp->data, req->data, req->data_len)) { 413 + pr_err("response data doesn't match expectation\n"); 414 + ret = -EINVAL; 415 + goto out; 416 + } 417 + 418 + ret = count; 419 + 420 + out: 421 + kfree(resp); 422 + kfree(req); 423 + 424 + return ret; 425 + } 426 + 427 + static const struct file_operations data_fops = { 428 + .open = simple_open, 429 + .write = data_write, 430 + }; 431 + 432 + static struct qmi_msg_handler qmi_sample_handlers[] = { 433 + { 434 + .type = QMI_RESPONSE, 435 + .msg_id = TEST_PING_REQ_MSG_ID_V01, 436 + .ei = test_ping_resp_msg_v01_ei, 437 + .decoded_size = sizeof(struct test_ping_req_msg_v01), 438 + .fn = ping_pong_cb 439 + }, 440 + {} 441 + }; 442 + 443 + struct qmi_sample { 444 + struct qmi_handle qmi; 445 + 446 + struct dentry *de_dir; 447 + struct dentry *de_data; 448 + struct dentry *de_ping; 449 + }; 450 + 451 + static struct dentry *qmi_debug_dir; 452 + 453 + static int qmi_sample_probe(struct platform_device *pdev) 454 + { 455 + struct sockaddr_qrtr *sq; 456 + struct qmi_sample *sample; 457 + char path[20]; 458 + int ret; 459 + 460 + sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL); 461 + if (!sample) 462 + return -ENOMEM; 463 + 464 + ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01, 465 + NULL, 466 + qmi_sample_handlers); 467 + if (ret < 0) 468 + return ret; 469 + 470 + sq = dev_get_platdata(&pdev->dev); 471 + ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq, 472 + sizeof(*sq), 0); 473 + if (ret < 0) { 474 + pr_err("failed to connect to remote service port\n"); 475 + goto err_release_qmi_handle; 476 + } 477 + 478 + snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port); 479 + 480 + sample->de_dir = debugfs_create_dir(path, qmi_debug_dir); 481 + if (IS_ERR(sample->de_dir)) { 482 + ret = PTR_ERR(sample->de_dir); 483 + goto err_release_qmi_handle; 484 + } 485 + 486 + sample->de_data = debugfs_create_file("data", 0600, sample->de_dir, 487 + sample, &data_fops); 488 + if (IS_ERR(sample->de_data)) { 489 + ret = PTR_ERR(sample->de_data); 490 + goto err_remove_de_dir; 491 + } 492 + 493 + sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir, 494 + sample, &ping_fops); 495 + if (IS_ERR(sample->de_ping)) { 496 + ret = PTR_ERR(sample->de_ping); 497 + goto err_remove_de_data; 498 + } 499 + 500 + platform_set_drvdata(pdev, sample); 501 + 502 + return 0; 503 + 504 + err_remove_de_data: 505 + debugfs_remove(sample->de_data); 506 + err_remove_de_dir: 507 + debugfs_remove(sample->de_dir); 508 + err_release_qmi_handle: 509 + qmi_handle_release(&sample->qmi); 510 + 511 + return ret; 512 + } 513 + 514 + static int qmi_sample_remove(struct platform_device *pdev) 515 + { 516 + struct qmi_sample *sample = platform_get_drvdata(pdev); 517 + 518 + debugfs_remove(sample->de_ping); 519 + debugfs_remove(sample->de_data); 520 + debugfs_remove(sample->de_dir); 521 + 522 + qmi_handle_release(&sample->qmi); 523 + 524 + return 0; 525 + } 526 + 527 + static struct platform_driver qmi_sample_driver = { 528 + .probe = qmi_sample_probe, 529 + .remove = qmi_sample_remove, 530 + .driver = { 531 + .name = "qmi_sample_client", 532 + }, 533 + }; 534 + 535 + static int qmi_sample_new_server(struct qmi_handle *qmi, 536 + struct qmi_service *service) 537 + { 538 + struct platform_device *pdev; 539 + struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port }; 540 + int ret; 541 + 542 + pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO); 543 + if (!pdev) 544 + return -ENOMEM; 545 + 546 + ret = platform_device_add_data(pdev, &sq, sizeof(sq)); 547 + if (ret) 548 + goto err_put_device; 549 + 550 + ret = platform_device_add(pdev); 551 + if (ret) 552 + goto err_put_device; 553 + 554 + service->priv = pdev; 555 + 556 + return 0; 557 + 558 + err_put_device: 559 + platform_device_put(pdev); 560 + 561 + return ret; 562 + } 563 + 564 + static void qmi_sample_del_server(struct qmi_handle *qmi, 565 + struct qmi_service *service) 566 + { 567 + struct platform_device *pdev = service->priv; 568 + 569 + platform_device_unregister(pdev); 570 + } 571 + 572 + static struct qmi_handle lookup_client; 573 + 574 + static struct qmi_ops lookup_ops = { 575 + .new_server = qmi_sample_new_server, 576 + .del_server = qmi_sample_del_server, 577 + }; 578 + 579 + static int qmi_sample_init(void) 580 + { 581 + int ret; 582 + 583 + qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL); 584 + if (IS_ERR(qmi_debug_dir)) { 585 + pr_err("failed to create qmi_sample dir\n"); 586 + return PTR_ERR(qmi_debug_dir); 587 + } 588 + 589 + ret = platform_driver_register(&qmi_sample_driver); 590 + if (ret) 591 + goto err_remove_debug_dir; 592 + 593 + ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL); 594 + if (ret < 0) 595 + goto err_unregister_driver; 596 + 597 + qmi_add_lookup(&lookup_client, 15, 0, 0); 598 + 599 + return 0; 600 + 601 + err_unregister_driver: 602 + platform_driver_unregister(&qmi_sample_driver); 603 + err_remove_debug_dir: 604 + debugfs_remove(qmi_debug_dir); 605 + 606 + return ret; 607 + } 608 + 609 + static void qmi_sample_exit(void) 610 + { 611 + qmi_handle_release(&lookup_client); 612 + 613 + platform_driver_unregister(&qmi_sample_driver); 614 + 615 + debugfs_remove(qmi_debug_dir); 616 + } 617 + 618 + module_init(qmi_sample_init); 619 + module_exit(qmi_sample_exit); 620 + 621 + MODULE_DESCRIPTION("Sample QMI client driver"); 622 + MODULE_LICENSE("GPL v2");