remoteproc/omap: Add support to parse internal memories from DT
[sfrench/cifs-2.6.git] / drivers / remoteproc / remoteproc_core.c
index 097f33e4f1f33833fb1101f55990aad7d6d69cad..e12a54e67588c3b52634ee69d39bc986033b1b5e 100644 (file)
@@ -16,6 +16,7 @@
 
 #define pr_fmt(fmt)    "%s: " fmt, __func__
 
+#include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/device.h>
@@ -26,6 +27,7 @@
 #include <linux/string.h>
 #include <linux/debugfs.h>
 #include <linux/devcoredump.h>
+#include <linux/rculist.h>
 #include <linux/remoteproc.h>
 #include <linux/iommu.h>
 #include <linux/idr.h>
 #include <linux/platform_device.h>
 
 #include "remoteproc_internal.h"
+#include "remoteproc_elf_helpers.h"
 
 #define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL
 
 static DEFINE_MUTEX(rproc_list_mutex);
 static LIST_HEAD(rproc_list);
+static struct notifier_block rproc_panic_nb;
 
 typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
                                 void *, int offset, int avail);
@@ -185,7 +189,7 @@ EXPORT_SYMBOL(rproc_va_to_pa);
  * here the output of the DMA API for the carveouts, which should be more
  * correct.
  */
-void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
 {
        struct rproc_mem_entry *carveout;
        void *ptr = NULL;
@@ -224,7 +228,8 @@ EXPORT_SYMBOL(rproc_da_to_va);
 /**
  * rproc_find_carveout_by_name() - lookup the carveout region by a name
  * @rproc: handle of a remote processor
- * @name,..: carveout name to find (standard printf format)
+ * @name: carveout name to find (format string)
+ * @...: optional parameters matching @name string
  *
  * Platform driver has the capability to register some pre-allacoted carveout
  * (physically contiguous memory regions) before rproc firmware loading and
@@ -318,8 +323,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        struct device *dev = &rproc->dev;
        struct rproc_vring *rvring = &rvdev->vring[i];
        struct fw_rsc_vdev *rsc;
-       int ret, size, notifyid;
+       int ret, notifyid;
        struct rproc_mem_entry *mem;
+       size_t size;
 
        /* actual size of vring (in bytes) */
        size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
@@ -445,6 +451,7 @@ static void rproc_rvdev_release(struct device *dev)
  * rproc_handle_vdev() - handle a vdev fw resource
  * @rproc: the remote processor
  * @rsc: the vring resource descriptor
+ * @offset: offset of the resource entry
  * @avail: size of available data (for sanity checking the image)
  *
  * This resource entry requests the host to statically register a virtio
@@ -587,6 +594,7 @@ void rproc_vdev_release(struct kref *ref)
  * rproc_handle_trace() - handle a shared trace buffer resource
  * @rproc: the remote processor
  * @rsc: the trace resource descriptor
+ * @offset: offset of the resource entry
  * @avail: size of available data (for sanity checking the image)
  *
  * In case the remote processor dumps trace logs into memory,
@@ -652,6 +660,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
  * rproc_handle_devmem() - handle devmem resource entry
  * @rproc: remote processor handle
  * @rsc: the devmem resource entry
+ * @offset: offset of the resource entry
  * @avail: size of available data (for sanity checking the image)
  *
  * Remote processors commonly need to access certain on-chip peripherals.
@@ -746,11 +755,12 @@ static int rproc_alloc_carveout(struct rproc *rproc,
        va = dma_alloc_coherent(dev->parent, mem->len, &dma, GFP_KERNEL);
        if (!va) {
                dev_err(dev->parent,
-                       "failed to allocate dma memory: len 0x%x\n", mem->len);
+                       "failed to allocate dma memory: len 0x%zx\n",
+                       mem->len);
                return -ENOMEM;
        }
 
-       dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n",
+       dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%zx\n",
                va, &dma, mem->len);
 
        if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) {
@@ -853,6 +863,7 @@ static int rproc_release_carveout(struct rproc *rproc,
  * rproc_handle_carveout() - handle phys contig memory allocation requests
  * @rproc: rproc handle
  * @rsc: the resource entry
+ * @offset: offset of the resource entry
  * @avail: size of available data (for image validation)
  *
  * This function will handle firmware requests for allocation of physically
@@ -957,7 +968,7 @@ EXPORT_SYMBOL(rproc_add_carveout);
  */
 struct rproc_mem_entry *
 rproc_mem_entry_init(struct device *dev,
-                    void *va, dma_addr_t dma, int len, u32 da,
+                    void *va, dma_addr_t dma, size_t len, u32 da,
                     int (*alloc)(struct rproc *, struct rproc_mem_entry *),
                     int (*release)(struct rproc *, struct rproc_mem_entry *),
                     const char *name, ...)
@@ -999,7 +1010,7 @@ EXPORT_SYMBOL(rproc_mem_entry_init);
  * provided by client.
  */
 struct rproc_mem_entry *
-rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len,
+rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len,
                             u32 da, const char *name, ...)
 {
        struct rproc_mem_entry *mem;
@@ -1022,7 +1033,7 @@ rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len,
 }
 EXPORT_SYMBOL(rproc_of_resm_mem_entry_init);
 
-/**
+/*
  * A lookup table for resource handlers. The indices are defined in
  * enum fw_resource_type.
  */
@@ -1270,7 +1281,7 @@ static void rproc_resource_cleanup(struct rproc *rproc)
                unmapped = iommu_unmap(rproc->domain, entry->da, entry->len);
                if (unmapped != entry->len) {
                        /* nothing much to do besides complaining */
-                       dev_err(dev, "failed to unmap %u/%zu\n", entry->len,
+                       dev_err(dev, "failed to unmap %zx/%zu\n", entry->len,
                                unmapped);
                }
 
@@ -1564,20 +1575,21 @@ EXPORT_SYMBOL(rproc_coredump_add_custom_segment);
 static void rproc_coredump(struct rproc *rproc)
 {
        struct rproc_dump_segment *segment;
-       struct elf32_phdr *phdr;
-       struct elf32_hdr *ehdr;
+       void *phdr;
+       void *ehdr;
        size_t data_size;
        size_t offset;
        void *data;
        void *ptr;
+       u8 class = rproc->elf_class;
        int phnum = 0;
 
        if (list_empty(&rproc->dump_segments))
                return;
 
-       data_size = sizeof(*ehdr);
+       data_size = elf_size_of_hdr(class);
        list_for_each_entry(segment, &rproc->dump_segments, node) {
-               data_size += sizeof(*phdr) + segment->size;
+               data_size += elf_size_of_phdr(class) + segment->size;
 
                phnum++;
        }
@@ -1588,33 +1600,33 @@ static void rproc_coredump(struct rproc *rproc)
 
        ehdr = data;
 
-       memset(ehdr, 0, sizeof(*ehdr));
-       memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
-       ehdr->e_ident[EI_CLASS] = ELFCLASS32;
-       ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
-       ehdr->e_ident[EI_VERSION] = EV_CURRENT;
-       ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
-       ehdr->e_type = ET_CORE;
-       ehdr->e_machine = EM_NONE;
-       ehdr->e_version = EV_CURRENT;
-       ehdr->e_entry = rproc->bootaddr;
-       ehdr->e_phoff = sizeof(*ehdr);
-       ehdr->e_ehsize = sizeof(*ehdr);
-       ehdr->e_phentsize = sizeof(*phdr);
-       ehdr->e_phnum = phnum;
-
-       phdr = data + ehdr->e_phoff;
-       offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum;
+       memset(ehdr, 0, elf_size_of_hdr(class));
+       /* e_ident field is common for both elf32 and elf64 */
+       elf_hdr_init_ident(ehdr, class);
+
+       elf_hdr_set_e_type(class, ehdr, ET_CORE);
+       elf_hdr_set_e_machine(class, ehdr, EM_NONE);
+       elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
+       elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
+       elf_hdr_set_e_phoff(class, ehdr, elf_size_of_hdr(class));
+       elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
+       elf_hdr_set_e_phentsize(class, ehdr, elf_size_of_phdr(class));
+       elf_hdr_set_e_phnum(class, ehdr, phnum);
+
+       phdr = data + elf_hdr_get_e_phoff(class, ehdr);
+       offset = elf_hdr_get_e_phoff(class, ehdr);
+       offset += elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr);
+
        list_for_each_entry(segment, &rproc->dump_segments, node) {
-               memset(phdr, 0, sizeof(*phdr));
-               phdr->p_type = PT_LOAD;
-               phdr->p_offset = offset;
-               phdr->p_vaddr = segment->da;
-               phdr->p_paddr = segment->da;
-               phdr->p_filesz = segment->size;
-               phdr->p_memsz = segment->size;
-               phdr->p_flags = PF_R | PF_W | PF_X;
-               phdr->p_align = 0;
+               memset(phdr, 0, elf_size_of_phdr(class));
+               elf_phdr_set_p_type(class, phdr, PT_LOAD);
+               elf_phdr_set_p_offset(class, phdr, offset);
+               elf_phdr_set_p_vaddr(class, phdr, segment->da);
+               elf_phdr_set_p_paddr(class, phdr, segment->da);
+               elf_phdr_set_p_filesz(class, phdr, segment->size);
+               elf_phdr_set_p_memsz(class, phdr, segment->size);
+               elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X);
+               elf_phdr_set_p_align(class, phdr, 0);
 
                if (segment->dump) {
                        segment->dump(rproc, segment, data + offset);
@@ -1630,8 +1642,8 @@ static void rproc_coredump(struct rproc *rproc)
                        }
                }
 
-               offset += phdr->p_filesz;
-               phdr++;
+               offset += elf_phdr_get_p_filesz(class, phdr);
+               phdr += elf_size_of_phdr(class);
        }
 
        dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
@@ -1653,12 +1665,16 @@ int rproc_trigger_recovery(struct rproc *rproc)
        struct device *dev = &rproc->dev;
        int ret;
 
-       dev_err(dev, "recovering %s\n", rproc->name);
-
        ret = mutex_lock_interruptible(&rproc->lock);
        if (ret)
                return ret;
 
+       /* State could have changed before we got the mutex */
+       if (rproc->state != RPROC_CRASHED)
+               goto unlock_mutex;
+
+       dev_err(dev, "recovering %s\n", rproc->name);
+
        ret = rproc_stop(rproc, true);
        if (ret)
                goto unlock_mutex;
@@ -1685,6 +1701,7 @@ unlock_mutex:
 
 /**
  * rproc_crash_handler_work() - handle a crash
+ * @work: work treating the crash
  *
  * This function needs to handle everything related to a crash, like cpu
  * registers and stack dump, information to help to debug the fatal error, etc.
@@ -1854,8 +1871,8 @@ struct rproc *rproc_get_by_phandle(phandle phandle)
        if (!np)
                return NULL;
 
-       mutex_lock(&rproc_list_mutex);
-       list_for_each_entry(r, &rproc_list, node) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(r, &rproc_list, node) {
                if (r->dev.parent && r->dev.parent->of_node == np) {
                        /* prevent underlying implementation from being removed */
                        if (!try_module_get(r->dev.parent->driver->owner)) {
@@ -1868,7 +1885,7 @@ struct rproc *rproc_get_by_phandle(phandle phandle)
                        break;
                }
        }
-       mutex_unlock(&rproc_list_mutex);
+       rcu_read_unlock();
 
        of_node_put(np);
 
@@ -1925,7 +1942,7 @@ int rproc_add(struct rproc *rproc)
 
        /* expose to rproc_get_by_phandle users */
        mutex_lock(&rproc_list_mutex);
-       list_add(&rproc->node, &rproc_list);
+       list_add_rcu(&rproc->node, &rproc_list);
        mutex_unlock(&rproc_list_mutex);
 
        return 0;
@@ -2029,6 +2046,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
        rproc->name = name;
        rproc->priv = &rproc[1];
        rproc->auto_boot = true;
+       rproc->elf_class = ELFCLASS32;
 
        device_initialize(&rproc->dev);
        rproc->dev.parent = dev;
@@ -2053,7 +2071,8 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
                rproc->ops->load = rproc_elf_load_segments;
                rproc->ops->parse_fw = rproc_elf_load_rsc_table;
                rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
-               rproc->ops->sanity_check = rproc_elf_sanity_check;
+               if (!rproc->ops->sanity_check)
+                       rproc->ops->sanity_check = rproc_elf32_sanity_check;
                rproc->ops->get_boot_addr = rproc_elf_get_boot_addr;
        }
 
@@ -2140,9 +2159,12 @@ int rproc_del(struct rproc *rproc)
 
        /* the rproc is downref'ed as soon as it's removed from the klist */
        mutex_lock(&rproc_list_mutex);
-       list_del(&rproc->node);
+       list_del_rcu(&rproc->node);
        mutex_unlock(&rproc_list_mutex);
 
+       /* Ensure that no readers of rproc_list are still active */
+       synchronize_rcu();
+
        device_del(&rproc->dev);
 
        return 0;
@@ -2216,10 +2238,50 @@ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
 }
 EXPORT_SYMBOL(rproc_report_crash);
 
+static int rproc_panic_handler(struct notifier_block *nb, unsigned long event,
+                              void *ptr)
+{
+       unsigned int longest = 0;
+       struct rproc *rproc;
+       unsigned int d;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(rproc, &rproc_list, node) {
+               if (!rproc->ops->panic || rproc->state != RPROC_RUNNING)
+                       continue;
+
+               d = rproc->ops->panic(rproc);
+               longest = max(longest, d);
+       }
+       rcu_read_unlock();
+
+       /*
+        * Delay for the longest requested duration before returning. This can
+        * be used by the remoteproc drivers to give the remote processor time
+        * to perform any requested operations (such as flush caches), when
+        * it's not possible to signal the Linux side due to the panic.
+        */
+       mdelay(longest);
+
+       return NOTIFY_DONE;
+}
+
+static void __init rproc_init_panic(void)
+{
+       rproc_panic_nb.notifier_call = rproc_panic_handler;
+       atomic_notifier_chain_register(&panic_notifier_list, &rproc_panic_nb);
+}
+
+static void __exit rproc_exit_panic(void)
+{
+       atomic_notifier_chain_unregister(&panic_notifier_list, &rproc_panic_nb);
+}
+
 static int __init remoteproc_init(void)
 {
        rproc_init_sysfs();
        rproc_init_debugfs();
+       rproc_init_panic();
 
        return 0;
 }
@@ -2229,6 +2291,7 @@ static void __exit remoteproc_exit(void)
 {
        ida_destroy(&rproc_dev_index);
 
+       rproc_exit_panic();
        rproc_exit_debugfs();
        rproc_exit_sysfs();
 }