Merge tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad...
[firefly-linux-kernel-4.4.55.git] / drivers / remoteproc / remoteproc_core.c
index 814af5ab8a72d2d5e979855bbffc86b487fe41f6..022dc635d01e4935ee84c0378e0d606e94028a39 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/iommu.h>
 #include <linux/idr.h>
 #include <linux/elf.h>
+#include <linux/crc32.h>
 #include <linux/virtio_ids.h>
 #include <linux/virtio_ring.h>
 #include <asm/byteorder.h>
@@ -45,7 +46,8 @@
 
 typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
                                struct resource_table *table, int len);
-typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
+typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
+                                void *, int offset, int avail);
 
 /* Unique indices for remoteproc devices */
 static DEFINE_IDA(rproc_dev_index);
@@ -192,6 +194,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        struct rproc *rproc = rvdev->rproc;
        struct device *dev = &rproc->dev;
        struct rproc_vring *rvring = &rvdev->vring[i];
+       struct fw_rsc_vdev *rsc;
        dma_addr_t dma;
        void *va;
        int ret, size, notifyid;
@@ -202,7 +205,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        /*
         * Allocate non-cacheable memory for the vring. In the future
         * this call will also configure the IOMMU for us
-        * TODO: let the rproc know the da of this vring
         */
        va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL);
        if (!va) {
@@ -213,7 +215,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        /*
         * Assign an rproc-wide unique index for this vring
         * TODO: assign a notifyid for rvdev updates as well
-        * TODO: let the rproc know the notifyid of this vring
         * TODO: support predefined notifyids (via resource table)
         */
        ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL);
@@ -224,9 +225,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        }
        notifyid = ret;
 
-       /* Store largest notifyid */
-       rproc->max_notifyid = max(rproc->max_notifyid, notifyid);
-
        dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
                                (unsigned long long)dma, size, notifyid);
 
@@ -234,6 +232,15 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        rvring->dma = dma;
        rvring->notifyid = notifyid;
 
+       /*
+        * Let the rproc know the notifyid and da of this vring.
+        * Not all platforms use dma_alloc_coherent to automatically
+        * set up the iommu. In this case the device address (da) will
+        * hold the physical address and not the device address.
+        */
+       rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
+       rsc->vring[i].da = dma;
+       rsc->vring[i].notifyid = notifyid;
        return 0;
 }
 
@@ -268,25 +275,20 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
        return 0;
 }
 
-static int rproc_max_notifyid(int id, void *p, void *data)
-{
-       int *maxid = data;
-       *maxid = max(*maxid, id);
-       return 0;
-}
-
 void rproc_free_vring(struct rproc_vring *rvring)
 {
        int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
        struct rproc *rproc = rvring->rvdev->rproc;
-       int maxid = 0;
+       int idx = rvring->rvdev->vring - rvring;
+       struct fw_rsc_vdev *rsc;
 
        dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma);
        idr_remove(&rproc->notifyids, rvring->notifyid);
 
-       /* Find the largest remaining notifyid */
-       idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid);
-       rproc->max_notifyid = maxid;
+       /* reset resource entry info */
+       rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset;
+       rsc->vring[idx].da = 0;
+       rsc->vring[idx].notifyid = -1;
 }
 
 /**
@@ -317,7 +319,7 @@ void rproc_free_vring(struct rproc_vring *rvring)
  * Returns 0 on success, or an appropriate error code otherwise
  */
 static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
 {
        struct device *dev = &rproc->dev;
        struct rproc_vdev *rvdev;
@@ -358,8 +360,8 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
                        goto free_rvdev;
        }
 
-       /* remember the device features */
-       rvdev->dfeatures = rsc->dfeatures;
+       /* remember the resource offset*/
+       rvdev->rsc_offset = offset;
 
        list_add_tail(&rvdev->node, &rproc->rvdevs);
 
@@ -394,7 +396,7 @@ free_rvdev:
  * Returns 0 on success, or an appropriate error code otherwise
  */
 static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
 {
        struct rproc_mem_entry *trace;
        struct device *dev = &rproc->dev;
@@ -476,7 +478,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
  * are outside those ranges.
  */
 static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
 {
        struct rproc_mem_entry *mapping;
        struct device *dev = &rproc->dev;
@@ -549,7 +551,9 @@ out:
  * pressure is important; it may have a substantial impact on performance.
  */
 static int rproc_handle_carveout(struct rproc *rproc,
-                               struct fw_rsc_carveout *rsc, int avail)
+                                               struct fw_rsc_carveout *rsc,
+                                               int offset, int avail)
+
 {
        struct rproc_mem_entry *carveout, *mapping;
        struct device *dev = &rproc->dev;
@@ -671,28 +675,45 @@ free_carv:
        return ret;
 }
 
+static int rproc_count_vrings(struct rproc *rproc, struct fw_rsc_vdev *rsc,
+                             int offset, int avail)
+{
+       /* Summarize the number of notification IDs */
+       rproc->max_notifyid += rsc->num_of_vrings;
+
+       return 0;
+}
+
 /*
  * A lookup table for resource handlers. The indices are defined in
  * enum fw_resource_type.
  */
-static rproc_handle_resource_t rproc_handle_rsc[] = {
+static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
        [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
        [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
        [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
        [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
 };
 
+static rproc_handle_resource_t rproc_vdev_handler[RSC_LAST] = {
+       [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
+};
+
+static rproc_handle_resource_t rproc_count_vrings_handler[RSC_LAST] = {
+       [RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings,
+};
+
 /* handle firmware resource entries before booting the remote processor */
-static int
-rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
+static int rproc_handle_resources(struct rproc *rproc, int len,
+                                 rproc_handle_resource_t handlers[RSC_LAST])
 {
        struct device *dev = &rproc->dev;
        rproc_handle_resource_t handler;
        int ret = 0, i;
 
-       for (i = 0; i < table->num; i++) {
-               int offset = table->offset[i];
-               struct fw_rsc_hdr *hdr = (void *)table + offset;
+       for (i = 0; i < rproc->table_ptr->num; i++) {
+               int offset = rproc->table_ptr->offset[i];
+               struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
                int avail = len - offset - sizeof(*hdr);
                void *rsc = (void *)hdr + sizeof(*hdr);
 
@@ -709,45 +730,11 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len
                        continue;
                }
 
-               handler = rproc_handle_rsc[hdr->type];
+               handler = handlers[hdr->type];
                if (!handler)
                        continue;
 
-               ret = handler(rproc, rsc, avail);
-               if (ret)
-                       break;
-       }
-
-       return ret;
-}
-
-/* handle firmware resource entries while registering the remote processor */
-static int
-rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
-{
-       struct device *dev = &rproc->dev;
-       int ret = 0, i;
-
-       for (i = 0; i < table->num; i++) {
-               int offset = table->offset[i];
-               struct fw_rsc_hdr *hdr = (void *)table + offset;
-               int avail = len - offset - sizeof(*hdr);
-               struct fw_rsc_vdev *vrsc;
-
-               /* make sure table isn't truncated */
-               if (avail < 0) {
-                       dev_err(dev, "rsc table is truncated\n");
-                       return -EINVAL;
-               }
-
-               dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
-
-               if (hdr->type != RSC_VDEV)
-                       continue;
-
-               vrsc = (struct fw_rsc_vdev *)hdr->data;
-
-               ret = rproc_handle_vdev(rproc, vrsc, avail);
+               ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
                if (ret)
                        break;
        }
@@ -805,9 +792,12 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
 {
        struct device *dev = &rproc->dev;
        const char *name = rproc->firmware;
-       struct resource_table *table;
+       struct resource_table *table, *loaded_table;
        int ret, tablesz;
 
+       if (!rproc->table_ptr)
+               return -ENOMEM;
+
        ret = rproc_fw_sanity_check(rproc, fw);
        if (ret)
                return ret;
@@ -833,8 +823,15 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
                goto clean_up;
        }
 
+       /* Verify that resource table in loaded fw is unchanged */
+       if (rproc->table_csum != crc32(0, table, tablesz)) {
+               dev_err(dev, "resource checksum failed, fw changed?\n");
+               ret = -EINVAL;
+               goto clean_up;
+       }
+
        /* handle fw resources which are required to boot rproc */
-       ret = rproc_handle_boot_rsc(rproc, table, tablesz);
+       ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
        if (ret) {
                dev_err(dev, "Failed to process resources: %d\n", ret);
                goto clean_up;
@@ -847,6 +844,19 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
                goto clean_up;
        }
 
+       /*
+        * The starting device has been given the rproc->cached_table as the
+        * resource table. The address of the vring along with the other
+        * allocated resources (carveouts etc) is stored in cached_table.
+        * In order to pass this information to the remote device we must
+        * copy this information to device memory.
+        */
+       loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
+       if (!loaded_table)
+               goto clean_up;
+
+       memcpy(loaded_table, rproc->cached_table, tablesz);
+
        /* power up the remote processor */
        ret = rproc->ops->start(rproc);
        if (ret) {
@@ -854,6 +864,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
                goto clean_up;
        }
 
+       /*
+        * Update table_ptr so that all subsequent vring allocations and
+        * virtio fields manipulation update the actual loaded resource table
+        * in device memory.
+        */
+       rproc->table_ptr = loaded_table;
+
        rproc->state = RPROC_RUNNING;
 
        dev_info(dev, "remote processor %s is now up\n", rproc->name);
@@ -888,11 +905,30 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
        if (!table)
                goto out;
 
-       /* look for virtio devices and register them */
-       ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
+       rproc->table_csum = crc32(0, table, tablesz);
+
+       /*
+        * Create a copy of the resource table. When a virtio device starts
+        * and calls vring_new_virtqueue() the address of the allocated vring
+        * will be stored in the cached_table. Before the device is started,
+        * cached_table will be copied into devic memory.
+        */
+       rproc->cached_table = kmalloc(tablesz, GFP_KERNEL);
+       if (!rproc->cached_table)
+               goto out;
+
+       memcpy(rproc->cached_table, table, tablesz);
+       rproc->table_ptr = rproc->cached_table;
+
+       /* count the number of notify-ids */
+       rproc->max_notifyid = -1;
+       ret = rproc_handle_resources(rproc, tablesz, rproc_count_vrings_handler);
        if (ret)
                goto out;
 
+       /* look for virtio devices and register them */
+       ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler);
+
 out:
        release_firmware(fw);
        /* allow rproc_del() contexts, if any, to proceed */
@@ -950,6 +986,9 @@ int rproc_trigger_recovery(struct rproc *rproc)
        /* wait until there is no more rproc users */
        wait_for_completion(&rproc->crash_comp);
 
+       /* Free the copy of the resource table */
+       kfree(rproc->cached_table);
+
        return rproc_add_virtio_devices(rproc);
 }
 
@@ -1105,6 +1144,9 @@ void rproc_shutdown(struct rproc *rproc)
 
        rproc_disable_iommu(rproc);
 
+       /* Give the next start a clean resource table */
+       rproc->table_ptr = rproc->cached_table;
+
        /* if in crash state, unlock crash handler */
        if (rproc->state == RPROC_CRASHED)
                complete_all(&rproc->crash_comp);
@@ -1196,11 +1238,11 @@ static struct device_type rproc_type = {
  * @dev: the underlying device
  * @name: name of this remote processor
  * @ops: platform-specific handlers (mainly start/stop)
- * @firmware: name of firmware file to load
+ * @firmware: name of firmware file to load, can be NULL
  * @len: length of private data needed by the rproc driver (in bytes)
  *
  * Allocates a new remote processor handle, but does not register
- * it yet.
+ * it yet. if @firmware is NULL, a default name is used.
  *
  * This function should be used by rproc implementations during initialization
  * of the remote processor.
@@ -1219,19 +1261,39 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
                                const char *firmware, int len)
 {
        struct rproc *rproc;
+       char *p, *template = "rproc-%s-fw";
+       int name_len = 0;
 
        if (!dev || !name || !ops)
                return NULL;
 
-       rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
+       if (!firmware)
+               /*
+                * Make room for default firmware name (minus %s plus '\0').
+                * If the caller didn't pass in a firmware name then
+                * construct a default name.  We're already glomming 'len'
+                * bytes onto the end of the struct rproc allocation, so do
+                * a few more for the default firmware name (but only if
+                * the caller doesn't pass one).
+                */
+               name_len = strlen(name) + strlen(template) - 2 + 1;
+
+       rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL);
        if (!rproc) {
                dev_err(dev, "%s: kzalloc failed\n", __func__);
                return NULL;
        }
 
+       if (!firmware) {
+               p = (char *)rproc + sizeof(struct rproc) + len;
+               snprintf(p, name_len, template, name);
+       } else {
+               p = (char *)firmware;
+       }
+
+       rproc->firmware = p;
        rproc->name = name;
        rproc->ops = ops;
-       rproc->firmware = firmware;
        rproc->priv = &rproc[1];
 
        device_initialize(&rproc->dev);
@@ -1315,6 +1377,9 @@ int rproc_del(struct rproc *rproc)
        list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node)
                rproc_remove_virtio_dev(rvdev);
 
+       /* Free the copy of the resource table */
+       kfree(rproc->cached_table);
+
        device_del(&rproc->dev);
 
        return 0;