Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Qualcomm Peripheral Image Loader helpers
0004  *
0005  * Copyright (C) 2016 Linaro Ltd
0006  * Copyright (C) 2015 Sony Mobile Communications Inc
0007  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
0008  */
0009 
0010 #include <linux/firmware.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/notifier.h>
0014 #include <linux/remoteproc.h>
0015 #include <linux/remoteproc/qcom_rproc.h>
0016 #include <linux/rpmsg/qcom_glink.h>
0017 #include <linux/rpmsg/qcom_smd.h>
0018 #include <linux/slab.h>
0019 #include <linux/soc/qcom/mdt_loader.h>
0020 #include <linux/soc/qcom/smem.h>
0021 
0022 #include "remoteproc_internal.h"
0023 #include "qcom_common.h"
0024 
0025 #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
0026 #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
0027 #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
0028 
0029 #define MAX_NUM_OF_SS           10
0030 #define MAX_REGION_NAME_LENGTH  16
0031 #define SBL_MINIDUMP_SMEM_ID    602
0032 #define MD_REGION_VALID     ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0)
0033 #define MD_SS_ENCR_DONE     ('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
0034 #define MD_SS_ENABLED       ('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0)
0035 
0036 /**
0037  * struct minidump_region - Minidump region
0038  * @name        : Name of the region to be dumped
0039  * @seq_num:        : Use to differentiate regions with same name.
0040  * @valid       : This entry to be dumped (if set to 1)
0041  * @address     : Physical address of region to be dumped
0042  * @size        : Size of the region
0043  */
0044 struct minidump_region {
0045     char    name[MAX_REGION_NAME_LENGTH];
0046     __le32  seq_num;
0047     __le32  valid;
0048     __le64  address;
0049     __le64  size;
0050 };
0051 
0052 /**
0053  * struct minidump_subsystem - Subsystem's SMEM Table of content
0054  * @status : Subsystem toc init status
0055  * @enabled : if set to 1, this region would be copied during coredump
0056  * @encryption_status: Encryption status for this subsystem
0057  * @encryption_required : Decides to encrypt the subsystem regions or not
0058  * @region_count : Number of regions added in this subsystem toc
0059  * @regions_baseptr : regions base pointer of the subsystem
0060  */
0061 struct minidump_subsystem {
0062     __le32  status;
0063     __le32  enabled;
0064     __le32  encryption_status;
0065     __le32  encryption_required;
0066     __le32  region_count;
0067     __le64  regions_baseptr;
0068 };
0069 
0070 /**
0071  * struct minidump_global_toc - Global Table of Content
0072  * @status : Global Minidump init status
0073  * @md_revision : Minidump revision
0074  * @enabled : Minidump enable status
0075  * @subsystems : Array of subsystems toc
0076  */
0077 struct minidump_global_toc {
0078     __le32              status;
0079     __le32              md_revision;
0080     __le32              enabled;
0081     struct minidump_subsystem   subsystems[MAX_NUM_OF_SS];
0082 };
0083 
0084 struct qcom_ssr_subsystem {
0085     const char *name;
0086     struct srcu_notifier_head notifier_list;
0087     struct list_head list;
0088 };
0089 
0090 static LIST_HEAD(qcom_ssr_subsystem_list);
0091 static DEFINE_MUTEX(qcom_ssr_subsys_lock);
0092 
0093 static void qcom_minidump_cleanup(struct rproc *rproc)
0094 {
0095     struct rproc_dump_segment *entry, *tmp;
0096 
0097     list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
0098         list_del(&entry->node);
0099         kfree(entry->priv);
0100         kfree(entry);
0101     }
0102 }
0103 
0104 static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsystem *subsystem)
0105 {
0106     struct minidump_region __iomem *ptr;
0107     struct minidump_region region;
0108     int seg_cnt, i;
0109     dma_addr_t da;
0110     size_t size;
0111     char *name;
0112 
0113     if (WARN_ON(!list_empty(&rproc->dump_segments))) {
0114         dev_err(&rproc->dev, "dump segment list already populated\n");
0115         return -EUCLEAN;
0116     }
0117 
0118     seg_cnt = le32_to_cpu(subsystem->region_count);
0119     ptr = ioremap((unsigned long)le64_to_cpu(subsystem->regions_baseptr),
0120               seg_cnt * sizeof(struct minidump_region));
0121     if (!ptr)
0122         return -EFAULT;
0123 
0124     for (i = 0; i < seg_cnt; i++) {
0125         memcpy_fromio(&region, ptr + i, sizeof(region));
0126         if (region.valid == MD_REGION_VALID) {
0127             name = kstrdup(region.name, GFP_KERNEL);
0128             if (!name) {
0129                 iounmap(ptr);
0130                 return -ENOMEM;
0131             }
0132             da = le64_to_cpu(region.address);
0133             size = le32_to_cpu(region.size);
0134             rproc_coredump_add_custom_segment(rproc, da, size, NULL, name);
0135         }
0136     }
0137 
0138     iounmap(ptr);
0139     return 0;
0140 }
0141 
0142 void qcom_minidump(struct rproc *rproc, unsigned int minidump_id)
0143 {
0144     int ret;
0145     struct minidump_subsystem *subsystem;
0146     struct minidump_global_toc *toc;
0147 
0148     /* Get Global minidump ToC*/
0149     toc = qcom_smem_get(QCOM_SMEM_HOST_ANY, SBL_MINIDUMP_SMEM_ID, NULL);
0150 
0151     /* check if global table pointer exists and init is set */
0152     if (IS_ERR(toc) || !toc->status) {
0153         dev_err(&rproc->dev, "Minidump TOC not found in SMEM\n");
0154         return;
0155     }
0156 
0157     /* Get subsystem table of contents using the minidump id */
0158     subsystem = &toc->subsystems[minidump_id];
0159 
0160     /**
0161      * Collect minidump if SS ToC is valid and segment table
0162      * is initialized in memory and encryption status is set.
0163      */
0164     if (subsystem->regions_baseptr == 0 ||
0165         le32_to_cpu(subsystem->status) != 1 ||
0166         le32_to_cpu(subsystem->enabled) != MD_SS_ENABLED ||
0167         le32_to_cpu(subsystem->encryption_status) != MD_SS_ENCR_DONE) {
0168         dev_err(&rproc->dev, "Minidump not ready, skipping\n");
0169         return;
0170     }
0171 
0172     ret = qcom_add_minidump_segments(rproc, subsystem);
0173     if (ret) {
0174         dev_err(&rproc->dev, "Failed with error: %d while adding minidump entries\n", ret);
0175         goto clean_minidump;
0176     }
0177     rproc_coredump_using_sections(rproc);
0178 clean_minidump:
0179     qcom_minidump_cleanup(rproc);
0180 }
0181 EXPORT_SYMBOL_GPL(qcom_minidump);
0182 
0183 static int glink_subdev_start(struct rproc_subdev *subdev)
0184 {
0185     struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
0186 
0187     glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
0188 
0189     return PTR_ERR_OR_ZERO(glink->edge);
0190 }
0191 
0192 static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed)
0193 {
0194     struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
0195 
0196     qcom_glink_smem_unregister(glink->edge);
0197     glink->edge = NULL;
0198 }
0199 
0200 static void glink_subdev_unprepare(struct rproc_subdev *subdev)
0201 {
0202     struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
0203 
0204     qcom_glink_ssr_notify(glink->ssr_name);
0205 }
0206 
0207 /**
0208  * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc
0209  * @rproc:  rproc handle to parent the subdevice
0210  * @glink:  reference to a GLINK subdev context
0211  * @ssr_name:   identifier of the associated remoteproc for ssr notifications
0212  */
0213 void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
0214                const char *ssr_name)
0215 {
0216     struct device *dev = &rproc->dev;
0217 
0218     glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge");
0219     if (!glink->node)
0220         return;
0221 
0222     glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL);
0223     if (!glink->ssr_name)
0224         return;
0225 
0226     glink->dev = dev;
0227     glink->subdev.start = glink_subdev_start;
0228     glink->subdev.stop = glink_subdev_stop;
0229     glink->subdev.unprepare = glink_subdev_unprepare;
0230 
0231     rproc_add_subdev(rproc, &glink->subdev);
0232 }
0233 EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
0234 
0235 /**
0236  * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc
0237  * @rproc:  rproc handle
0238  * @glink:  reference to a GLINK subdev context
0239  */
0240 void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
0241 {
0242     if (!glink->node)
0243         return;
0244 
0245     rproc_remove_subdev(rproc, &glink->subdev);
0246     kfree_const(glink->ssr_name);
0247     of_node_put(glink->node);
0248 }
0249 EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
0250 
0251 /**
0252  * qcom_register_dump_segments() - register segments for coredump
0253  * @rproc:  remoteproc handle
0254  * @fw:     firmware header
0255  *
0256  * Register all segments of the ELF in the remoteproc coredump segment list
0257  *
0258  * Return: 0 on success, negative errno on failure.
0259  */
0260 int qcom_register_dump_segments(struct rproc *rproc,
0261                 const struct firmware *fw)
0262 {
0263     const struct elf32_phdr *phdrs;
0264     const struct elf32_phdr *phdr;
0265     const struct elf32_hdr *ehdr;
0266     int ret;
0267     int i;
0268 
0269     ehdr = (struct elf32_hdr *)fw->data;
0270     phdrs = (struct elf32_phdr *)(ehdr + 1);
0271 
0272     for (i = 0; i < ehdr->e_phnum; i++) {
0273         phdr = &phdrs[i];
0274 
0275         if (phdr->p_type != PT_LOAD)
0276             continue;
0277 
0278         if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
0279             continue;
0280 
0281         if (!phdr->p_memsz)
0282             continue;
0283 
0284         ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
0285                          phdr->p_memsz);
0286         if (ret)
0287             return ret;
0288     }
0289 
0290     return 0;
0291 }
0292 EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
0293 
0294 static int smd_subdev_start(struct rproc_subdev *subdev)
0295 {
0296     struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
0297 
0298     smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
0299 
0300     return PTR_ERR_OR_ZERO(smd->edge);
0301 }
0302 
0303 static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed)
0304 {
0305     struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
0306 
0307     qcom_smd_unregister_edge(smd->edge);
0308     smd->edge = NULL;
0309 }
0310 
0311 /**
0312  * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc
0313  * @rproc:  rproc handle to parent the subdevice
0314  * @smd:    reference to a Qualcomm subdev context
0315  */
0316 void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
0317 {
0318     struct device *dev = &rproc->dev;
0319 
0320     smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge");
0321     if (!smd->node)
0322         return;
0323 
0324     smd->dev = dev;
0325     smd->subdev.start = smd_subdev_start;
0326     smd->subdev.stop = smd_subdev_stop;
0327 
0328     rproc_add_subdev(rproc, &smd->subdev);
0329 }
0330 EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
0331 
0332 /**
0333  * qcom_remove_smd_subdev() - remove the smd subdevice from rproc
0334  * @rproc:  rproc handle
0335  * @smd:    the SMD subdevice to remove
0336  */
0337 void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
0338 {
0339     if (!smd->node)
0340         return;
0341 
0342     rproc_remove_subdev(rproc, &smd->subdev);
0343     of_node_put(smd->node);
0344 }
0345 EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
0346 
0347 static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name)
0348 {
0349     struct qcom_ssr_subsystem *info;
0350 
0351     mutex_lock(&qcom_ssr_subsys_lock);
0352     /* Match in the global qcom_ssr_subsystem_list with name */
0353     list_for_each_entry(info, &qcom_ssr_subsystem_list, list)
0354         if (!strcmp(info->name, name))
0355             goto out;
0356 
0357     info = kzalloc(sizeof(*info), GFP_KERNEL);
0358     if (!info) {
0359         info = ERR_PTR(-ENOMEM);
0360         goto out;
0361     }
0362     info->name = kstrdup_const(name, GFP_KERNEL);
0363     srcu_init_notifier_head(&info->notifier_list);
0364 
0365     /* Add to global notification list */
0366     list_add_tail(&info->list, &qcom_ssr_subsystem_list);
0367 
0368 out:
0369     mutex_unlock(&qcom_ssr_subsys_lock);
0370     return info;
0371 }
0372 
0373 /**
0374  * qcom_register_ssr_notifier() - register SSR notification handler
0375  * @name:   Subsystem's SSR name
0376  * @nb:     notifier_block to be invoked upon subsystem's state change
0377  *
0378  * This registers the @nb notifier block as part the notifier chain for a
0379  * remoteproc associated with @name. The notifier block's callback
0380  * will be invoked when the remote processor's SSR events occur
0381  * (pre/post startup and pre/post shutdown).
0382  *
0383  * Return: a subsystem cookie on success, ERR_PTR on failure.
0384  */
0385 void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb)
0386 {
0387     struct qcom_ssr_subsystem *info;
0388 
0389     info = qcom_ssr_get_subsys(name);
0390     if (IS_ERR(info))
0391         return info;
0392 
0393     srcu_notifier_chain_register(&info->notifier_list, nb);
0394 
0395     return &info->notifier_list;
0396 }
0397 EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
0398 
0399 /**
0400  * qcom_unregister_ssr_notifier() - unregister SSR notification handler
0401  * @notify: subsystem cookie returned from qcom_register_ssr_notifier
0402  * @nb:     notifier_block to unregister
0403  *
0404  * This function will unregister the notifier from the particular notifier
0405  * chain.
0406  *
0407  * Return: 0 on success, %ENOENT otherwise.
0408  */
0409 int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb)
0410 {
0411     return srcu_notifier_chain_unregister(notify, nb);
0412 }
0413 EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
0414 
0415 static int ssr_notify_prepare(struct rproc_subdev *subdev)
0416 {
0417     struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0418     struct qcom_ssr_notify_data data = {
0419         .name = ssr->info->name,
0420         .crashed = false,
0421     };
0422 
0423     srcu_notifier_call_chain(&ssr->info->notifier_list,
0424                  QCOM_SSR_BEFORE_POWERUP, &data);
0425     return 0;
0426 }
0427 
0428 static int ssr_notify_start(struct rproc_subdev *subdev)
0429 {
0430     struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0431     struct qcom_ssr_notify_data data = {
0432         .name = ssr->info->name,
0433         .crashed = false,
0434     };
0435 
0436     srcu_notifier_call_chain(&ssr->info->notifier_list,
0437                  QCOM_SSR_AFTER_POWERUP, &data);
0438     return 0;
0439 }
0440 
0441 static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
0442 {
0443     struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0444     struct qcom_ssr_notify_data data = {
0445         .name = ssr->info->name,
0446         .crashed = crashed,
0447     };
0448 
0449     srcu_notifier_call_chain(&ssr->info->notifier_list,
0450                  QCOM_SSR_BEFORE_SHUTDOWN, &data);
0451 }
0452 
0453 static void ssr_notify_unprepare(struct rproc_subdev *subdev)
0454 {
0455     struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0456     struct qcom_ssr_notify_data data = {
0457         .name = ssr->info->name,
0458         .crashed = false,
0459     };
0460 
0461     srcu_notifier_call_chain(&ssr->info->notifier_list,
0462                  QCOM_SSR_AFTER_SHUTDOWN, &data);
0463 }
0464 
0465 /**
0466  * qcom_add_ssr_subdev() - register subdevice as restart notification source
0467  * @rproc:  rproc handle
0468  * @ssr:    SSR subdevice handle
0469  * @ssr_name:   identifier to use for notifications originating from @rproc
0470  *
0471  * As the @ssr is registered with the @rproc SSR events will be sent to all
0472  * registered listeners for the remoteproc when it's SSR events occur
0473  * (pre/post startup and pre/post shutdown).
0474  */
0475 void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
0476              const char *ssr_name)
0477 {
0478     struct qcom_ssr_subsystem *info;
0479 
0480     info = qcom_ssr_get_subsys(ssr_name);
0481     if (IS_ERR(info)) {
0482         dev_err(&rproc->dev, "Failed to add ssr subdevice\n");
0483         return;
0484     }
0485 
0486     ssr->info = info;
0487     ssr->subdev.prepare = ssr_notify_prepare;
0488     ssr->subdev.start = ssr_notify_start;
0489     ssr->subdev.stop = ssr_notify_stop;
0490     ssr->subdev.unprepare = ssr_notify_unprepare;
0491 
0492     rproc_add_subdev(rproc, &ssr->subdev);
0493 }
0494 EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
0495 
0496 /**
0497  * qcom_remove_ssr_subdev() - remove subdevice as restart notification source
0498  * @rproc:  rproc handle
0499  * @ssr:    SSR subdevice handle
0500  */
0501 void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
0502 {
0503     rproc_remove_subdev(rproc, &ssr->subdev);
0504     ssr->info = NULL;
0505 }
0506 EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
0507 
0508 MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
0509 MODULE_LICENSE("GPL v2");