aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/remoteproc.txt136
-rw-r--r--drivers/remoteproc/Kconfig3
-rw-r--r--drivers/remoteproc/Makefile2
-rw-r--r--drivers/remoteproc/omap_remoteproc.c11
-rw-r--r--drivers/remoteproc/remoteproc_core.c532
-rw-r--r--drivers/remoteproc/remoteproc_internal.h6
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c (renamed from drivers/remoteproc/remoteproc_rpmsg.c)162
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c46
-rw-r--r--include/linux/remoteproc.h339
9 files changed, 793 insertions, 444 deletions
diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt
index 23ff7349ffe..70a048cd3fa 100644
--- a/Documentation/remoteproc.txt
+++ b/Documentation/remoteproc.txt
@@ -20,6 +20,11 @@ platform-specific remoteproc drivers only need to provide a few low-level
handlers, and then all rpmsg drivers will then just work
(for more information about the virtio-based rpmsg bus and its drivers,
please read Documentation/rpmsg.txt).
+Registration of other types of virtio devices is now also possible. Firmwares
+just need to publish what kind of virtio devices do they support, and then
+remoteproc will add those devices. This makes it possible to reuse the
+existing virtio drivers with remote processor backends at a minimal development
+cost.
2. User API
@@ -136,8 +141,6 @@ int dummy_rproc_example(struct rproc *my_rproc)
If found, those virtio devices will be created and added, so as a result
of registering this remote processor, additional virtio drivers might get
probed.
- Currently, though, we only support a single RPMSG virtio vdev per remote
- processor.
int rproc_unregister(struct rproc *rproc)
- Unregister a remote processor, and decrement its refcount.
@@ -174,7 +177,7 @@ struct rproc_ops {
};
Every remoteproc implementation should at least provide the ->start and ->stop
-handlers. If rpmsg functionality is also desired, then the ->kick handler
+handlers. If rpmsg/virtio functionality is also desired, then the ->kick handler
should be provided as well.
The ->start() handler takes an rproc handle and should then power on the
@@ -221,43 +224,52 @@ resource entries that publish the existence of supported features
or configurations by the remote processor, such as trace buffers and
supported virtio devices (and their configurations).
-Currently the resource table is just an array of:
+The resource table begins with this header:
/**
- * struct fw_resource - describes an entry from the resource section
+ * struct resource_table - firmware resource table header
+ * @ver: version number
+ * @num: number of resource entries
+ * @reserved: reserved (must be zero)
+ * @offset: array of offsets pointing at the various resource entries
+ *
+ * The header of the resource table, as expressed by this structure,
+ * contains a version number (should we need to change this format in the
+ * future), the number of available resource entries, and their offsets
+ * in the table.
+ */
+struct resource_table {
+ u32 ver;
+ u32 num;
+ u32 reserved[2];
+ u32 offset[0];
+} __packed;
+
+Immediately following this header are the resource entries themselves,
+each of which begins with the following resource entry header:
+
+/**
+ * struct fw_rsc_hdr - firmware resource entry header
* @type: resource type
- * @id: index number of the resource
- * @da: device address of the resource
- * @pa: physical address of the resource
- * @len: size, in bytes, of the resource
- * @flags: properties of the resource, e.g. iommu protection required
- * @reserved: must be 0 atm
- * @name: name of resource
+ * @data: resource data
+ *
+ * Every resource entry begins with a 'struct fw_rsc_hdr' header providing
+ * its @type. The content of the entry itself will immediately follow
+ * this header, and it should be parsed according to the resource type.
*/
-struct fw_resource {
+struct fw_rsc_hdr {
u32 type;
- u32 id;
- u64 da;
- u64 pa;
- u32 len;
- u32 flags;
- u8 reserved[16];
- u8 name[48];
+ u8 data[0];
} __packed;
Some resources entries are mere announcements, where the host is informed
of specific remoteproc configuration. Other entries require the host to
-do something (e.g. reserve a requested resource) and possibly also reply
-by overwriting a member inside 'struct fw_resource' with info about the
-allocated resource.
-
-Different resource entries use different members of this struct,
-with different meanings. This is pretty limiting and error-prone,
-so the plan is to move to variable-length TLV-based resource entries,
-where each resource will begin with a type and length fields, followed by
-its own specific structure.
+do something (e.g. allocate a system resource). Sometimes a negotiation
+is expected, where the firmware requests a resource, and once allocated,
+the host should provide back its details (e.g. address of an allocated
+memory region).
-Here are the resource types that are currently being used:
+Here are the various resource types that are currently supported:
/**
* enum fw_resource_type - types of resource entries
@@ -266,59 +278,45 @@ Here are the resource types that are currently being used:
* memory region.
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
* @RSC_TRACE: announces the availability of a trace buffer into which
- * the remote processor will be writing logs. In this case,
- * 'da' indicates the device address where logs are written to,
- * and 'len' is the size of the trace buffer.
- * @RSC_VRING: request for allocation of a virtio vring (address should
- * be indicated in 'da', and 'len' should contain the number
- * of buffers supported by the vring).
- * @RSC_VIRTIO_DEV: announces support for a virtio device, and serves as
- * the virtio header. 'da' contains the virtio device
- * features, 'pa' holds the virtio guest features (host
- * will write them here after they're negotiated), 'len'
- * holds the virtio status, and 'flags' holds the virtio
- * device id (currently only VIRTIO_ID_RPMSG is supported).
+ * the remote processor will be writing logs.
+ * @RSC_VDEV: declare support for a virtio device, and serve as its
+ * virtio header.
+ * @RSC_LAST: just keep this one at the end
+ *
+ * Please note that these values are used as indices to the rproc_handle_rsc
+ * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
+ * check the validity of an index before the lookup table is accessed, so
+ * please update it as needed.
*/
enum fw_resource_type {
RSC_CARVEOUT = 0,
RSC_DEVMEM = 1,
RSC_TRACE = 2,
- RSC_VRING = 3,
- RSC_VIRTIO_DEV = 4,
- RSC_VIRTIO_CFG = 5,
+ RSC_VDEV = 3,
+ RSC_LAST = 4,
};
-Most of the resource entries share the basic idea of address/length
-negotiation with the host: the firmware usually asks for memory
-of size 'len' bytes, and the host needs to allocate it and provide
-the device/physical address (when relevant) in 'da'/'pa' respectively.
-
-If the firmware is compiled with hard coded device addresses, and
-can't handle dynamically allocated 'da' values, then the 'da' field
-will contain the expected device addresses (today we actually only support
-this scheme, as there aren't yet any use cases for dynamically allocated
-device addresses).
+For more details regarding a specific resource type, please see its
+dedicated structure in include/linux/remoteproc.h.
We also expect that platform-specific resource entries will show up
-at some point. When that happens, we could easily add a new RSC_PLAFORM
+at some point. When that happens, we could easily add a new RSC_PLATFORM
type, and hand those resources to the platform-specific rproc driver to handle.
7. Virtio and remoteproc
The firmware should provide remoteproc information about virtio devices
-that it supports, and their configurations: a RSC_VIRTIO_DEV resource entry
-should specify the virtio device id, and subsequent RSC_VRING resource entries
-should indicate the vring size (i.e. how many buffers do they support) and
-where should they be mapped (i.e. which device address). Note: the alignment
-between the consumer and producer parts of the vring is assumed to be 4096.
-
-At this point we only support a single virtio rpmsg device per remote
-processor, but the plan is to remove this limitation. In addition, once we
-move to TLV-based resource table, the plan is to have a single RSC_VIRTIO
-entry per supported virtio device, which will include the virtio header,
-the vrings information and the virtio config space.
-
-Of course, RSC_VIRTIO resource entries are only good enough for static
+that it supports, and their configurations: a RSC_VDEV resource entry
+should specify the virtio device id (as in virtio_ids.h), virtio features,
+virtio config space, vrings information, etc.
+
+When a new remote processor is registered, the remoteproc framework
+will look for its resource table and will register the virtio devices
+it supports. A firmware may support any number of virtio devices, and
+of any type (a single remote processor can also easily support several
+rpmsg virtio devices this way, if desired).
+
+Of course, RSC_VDEV resource entries are only good enough for static
allocation of virtio devices. Dynamic allocations will also be made possible
using the rpmsg bus (similar to how we already do dynamic allocations of
rpmsg channels; read more about it in rpmsg.txt).
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 25fc4ccbc2e..24d880e78ec 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -8,11 +8,10 @@ config REMOTEPROC
config OMAP_REMOTEPROC
tristate "OMAP remoteproc support"
depends on ARCH_OMAP4
- select OMAP_IOMMU
+ depends on OMAP_IOMMU
select REMOTEPROC
select OMAP_MBOX_FWK
select RPMSG
- default m
help
Say y here to support OMAP's remote processors (dual M3
and DSP on OMAP4) via the remote processor framework.
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index df0897f69e1..5445d9b2329 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -5,5 +5,5 @@
obj-$(CONFIG_REMOTEPROC) += remoteproc.o
remoteproc-y := remoteproc_core.o
remoteproc-y += remoteproc_debugfs.o
-remoteproc-y += remoteproc_rpmsg.o
+remoteproc-y += remoteproc_virtio.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index aa3ce52dc65..69425c4e86f 100644
--- a/drivers/remoteproc/omap_remoteproc.c
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -80,16 +80,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
dev_info(dev, "received echo reply from %s\n", name);
break;
default:
- /* ignore vq indices which are too large to be valid */
- if (msg >= 2) {
- dev_warn(dev, "invalid mbox msg: 0x%x\n", msg);
- break;
- }
-
- /*
- * At this point, 'msg' contains the index of the vring
- * which was just triggered.
- */
+ /* msg contains the index of the triggered vring */
if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
dev_dbg(dev, "no message was found in vqid %d\n", msg);
}
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 729911b67a9..ee15c68fb51 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -52,8 +52,8 @@ static void klist_rproc_put(struct klist_node *n);
* We need this in order to support name-based lookups (needed by the
* rproc_get_by_name()).
*
- * That said, we don't use rproc_get_by_name() anymore within the rpmsg
- * framework. The use cases that do require its existence should be
+ * That said, we don't use rproc_get_by_name() at this point.
+ * The use cases that do require its existence should be
* scrutinized, and hopefully migrated to rproc_boot() using device-based
* binding.
*
@@ -63,9 +63,8 @@ static void klist_rproc_put(struct klist_node *n);
static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put);
typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
- struct fw_resource *rsc, int len);
-typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
- struct fw_resource *rsc);
+ struct resource_table *table, int len);
+typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
/*
* This is the IOMMU fault handler we register with the IOMMU API
@@ -280,145 +279,182 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
return ret;
}
-/**
- * rproc_handle_virtio_hdr() - handle a virtio header resource
- * @rproc: the remote processor
- * @rsc: the resource descriptor
- *
- * The existence of this virtio hdr resource entry means that the firmware
- * of this @rproc supports this virtio device.
- *
- * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG,
- * but the plan is to remove this limitation and support any number
- * of virtio devices (and of any type). We'll also add support for dynamically
- * adding (and removing) virtio devices over the rpmsg bus, but small
- * firmwares that doesn't want to get involved with rpmsg will be able
- * to simple use the resource table for this.
- *
- * At this point this virtio header entry is rather simple: it just
- * announces the virtio device id and the supported virtio device features.
- * The plan though is to extend this to include the vring information and
- * the virtio config space, too (but first, some resource table overhaul
- * is needed: move from fixed-sized to variable-length TLV entries).
- *
- * For now, the 'flags' member of the resource entry contains the virtio
- * device id, the 'da' member contains the device features, and 'pa' is
- * where we need to store the guest features once negotiation completes.
- * As usual, the 'id' member of this resource contains the index of this
- * resource type (i.e. is this the first virtio hdr entry, the 2nd, ...).
- *
- * Returns 0 on success, or an appropriate error code otherwise
- */
-static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc)
+static int
+__rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
{
- struct rproc_vdev *rvdev;
+ struct rproc *rproc = rvdev->rproc;
+ struct device *dev = rproc->dev;
+ struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
+ dma_addr_t dma;
+ void *va;
+ int ret, size, notifyid;
+
+ dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n",
+ i, vring->da, vring->num, vring->align);
- /* we only support VIRTIO_ID_RPMSG devices for now */
- if (rsc->flags != VIRTIO_ID_RPMSG) {
- dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->flags);
+ /* make sure reserved bytes are zeroes */
+ if (vring->reserved) {
+ dev_err(dev, "vring rsc has non zero reserved bytes\n");
return -EINVAL;
}
- /* we only support a single vdev per rproc for now */
- if (rsc->id || rproc->rvdev) {
- dev_warn(rproc->dev, "redundant vdev entry: %s\n", rsc->name);
+ /* verify queue size and vring alignment are sane */
+ if (!vring->num || !vring->align) {
+ dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
+ vring->num, vring->align);
return -EINVAL;
}
- rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
- if (!rvdev)
+ /* actual size of vring (in bytes) */
+ size = PAGE_ALIGN(vring_size(vring->num, vring->align));
+
+ if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) {
+ dev_err(dev, "idr_pre_get failed\n");
return -ENOMEM;
+ }
- /* remember the device features */
- rvdev->dfeatures = rsc->da;
+ /*
+ * Allocate non-cacheable memory for the vring. In the future
+ * this call will also configure the IOMMU for us
+ */
+ va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL);
+ if (!va) {
+ dev_err(dev, "dma_alloc_coherent failed\n");
+ return -EINVAL;
+ }
- rproc->rvdev = rvdev;
- rvdev->rproc = rproc;
+ /* assign an rproc-wide unique index for this vring */
+ /* TODO: assign a notifyid for rvdev updates as well */
+ ret = idr_get_new(&rproc->notifyids, &rvdev->vring[i], &notifyid);
+ if (ret) {
+ dev_err(dev, "idr_get_new failed: %d\n", ret);
+ dma_free_coherent(dev, size, va, dma);
+ return ret;
+ }
+
+ /* let the rproc know the da and notifyid of this vring */
+ /* TODO: expose this to remote processor */
+ vring->da = dma;
+ vring->notifyid = notifyid;
+
+ dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va,
+ dma, size, notifyid);
+
+ rvdev->vring[i].len = vring->num;
+ rvdev->vring[i].align = vring->align;
+ rvdev->vring[i].va = va;
+ rvdev->vring[i].dma = dma;
+ rvdev->vring[i].notifyid = notifyid;
+ rvdev->vring[i].rvdev = rvdev;
return 0;
}
+static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i)
+{
+ struct rproc *rproc = rvdev->rproc;
+
+ for (i--; i > 0; i--) {
+ struct rproc_vring *rvring = &rvdev->vring[i];
+ int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
+
+ dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma);
+ idr_remove(&rproc->notifyids, rvring->notifyid);
+ }
+}
+
/**
- * rproc_handle_vring() - handle a vring fw resource
+ * rproc_handle_vdev() - handle a vdev fw resource
* @rproc: the remote processor
* @rsc: the vring resource descriptor
- *
- * This resource entry requires allocation of non-cacheable memory
- * for a virtio vring. Currently we only support two vrings per remote
- * processor, required for the virtio rpmsg device.
- *
- * The 'len' member of @rsc should contain the number of buffers this vring
- * support and 'da' should either contain the device address where
- * the remote processor is expecting the vring, or indicate that
- * dynamically allocation of the vring's device address is supported.
- *
- * Note: 'da' is currently not handled. This will be revised when the generic
- * iommu-based DMA API will arrive, or a dynanic & non-iommu use case show
- * up. Meanwhile, statically-addressed iommu-based images should use
- * RSC_DEVMEM resource entries to map their require 'da' to the physical
- * address of their base CMA region.
+ * @avail: size of available data (for sanity checking the image)
+ *
+ * This resource entry requests the host to statically register a virtio
+ * device (vdev), and setup everything needed to support it. It contains
+ * everything needed to make it possible: the virtio device id, virtio
+ * device features, vrings information, virtio config space, etc...
+ *
+ * Before registering the vdev, the vrings are allocated from non-cacheable
+ * physically contiguous memory. Currently we only support two vrings per
+ * remote processor (temporary limitation). We might also want to consider
+ * doing the vring allocation only later when ->find_vqs() is invoked, and
+ * then release them upon ->del_vqs().
+ *
+ * Note: @da is currently not really handled correctly: we dynamically
+ * allocate it using the DMA API, ignoring requested hard coded addresses,
+ * and we don't take care of any required IOMMU programming. This is all
+ * going to be taken care of when the generic iommu-based DMA API will be
+ * merged. Meanwhile, statically-addressed iommu-based firmware images should
+ * use RSC_DEVMEM resource entries to map their required @da to the physical
+ * address of their base CMA region (ouch, hacky!).
*
* Returns 0 on success, or an appropriate error code otherwise
*/
-static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc)
+static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
+ int avail)
{
struct device *dev = rproc->dev;
- struct rproc_vdev *rvdev = rproc->rvdev;
- dma_addr_t dma;
- int size, id = rsc->id;
- void *va;
+ struct rproc_vdev *rvdev;
+ int i, ret;
- /* no vdev is in place ? */
- if (!rvdev) {
- dev_err(dev, "vring requested without a virtio dev entry\n");
+ /* make sure resource isn't truncated */
+ if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring)
+ + rsc->config_len > avail) {
+ dev_err(rproc->dev, "vdev rsc is truncated\n");
return -EINVAL;
}
- /* the firmware must provide the expected queue size */
- if (!rsc->len) {
- dev_err(dev, "missing expected queue size\n");
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved[0] || rsc->reserved[1]) {
+ dev_err(dev, "vdev rsc has non zero reserved bytes\n");
return -EINVAL;
}
- /* we currently support two vrings per rproc (for rx and tx) */
- if (id >= ARRAY_SIZE(rvdev->vring)) {
- dev_err(dev, "%s: invalid vring id %d\n", rsc->name, id);
- return -EINVAL;
- }
+ dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n",
+ rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);
- /* have we already allocated this vring id ? */
- if (rvdev->vring[id].len) {
- dev_err(dev, "%s: duplicated id %d\n", rsc->name, id);
+ /* we currently support only two vrings per rvdev */
+ if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) {
+ dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings);
return -EINVAL;
}
- /* actual size of vring (in bytes) */
- size = PAGE_ALIGN(vring_size(rsc->len, AMP_VRING_ALIGN));
-
- /*
- * Allocate non-cacheable memory for the vring. In the future
- * this call will also configure the IOMMU for us
- */
- va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL);
- if (!va) {
- dev_err(dev, "dma_alloc_coherent failed\n");
+ rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
+ if (!rvdev)
return -ENOMEM;
+
+ rvdev->rproc = rproc;
+
+ /* allocate the vrings */
+ for (i = 0; i < rsc->num_of_vrings; i++) {
+ ret = __rproc_handle_vring(rvdev, rsc, i);
+ if (ret)
+ goto free_vrings;
}
- dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", id, va,
- dma, rsc->len, size);
+ /* remember the device features */
+ rvdev->dfeatures = rsc->dfeatures;
+
+ list_add_tail(&rvdev->node, &rproc->rvdevs);
- rvdev->vring[id].len = rsc->len;
- rvdev->vring[id].va = va;
- rvdev->vring[id].dma = dma;
+ /* it is now safe to add the virtio device */
+ ret = rproc_add_virtio_dev(rvdev, rsc->id);
+ if (ret)
+ goto free_vrings;
return 0;
+
+free_vrings:
+ __rproc_free_vrings(rvdev, i);
+ kfree(rvdev);
+ return ret;
}
/**
* rproc_handle_trace() - handle a shared trace buffer resource
* @rproc: the remote processor
* @rsc: the trace resource descriptor
+ * @avail: size of available data (for sanity checking the image)
*
* In case the remote processor dumps trace logs into memory,
* export it via debugfs.
@@ -430,13 +466,25 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc)
*
* Returns 0 on success, or an appropriate error code otherwise
*/
-static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
+static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
+ int avail)
{
struct rproc_mem_entry *trace;
struct device *dev = rproc->dev;
void *ptr;
char name[15];
+ if (sizeof(*rsc) > avail) {
+ dev_err(rproc->dev, "trace rsc is truncated\n");
+ return -EINVAL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved) {
+ dev_err(dev, "trace rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
/* what's the kernel address of this resource ? */
ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
if (!ptr) {
@@ -469,7 +517,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
rproc->num_traces++;
- dev_dbg(dev, "%s added: va %p, da 0x%llx, len 0x%x\n", name, ptr,
+ dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr,
rsc->da, rsc->len);
return 0;
@@ -479,6 +527,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
* rproc_handle_devmem() - handle devmem resource entry
* @rproc: remote processor handle
* @rsc: the devmem resource entry
+ * @avail: size of available data (for sanity checking the image)
*
* Remote processors commonly need to access certain on-chip peripherals.
*
@@ -499,7 +548,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
* and not allow firmwares to request access to physical addresses that
* are outside those ranges.
*/
-static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
+static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
+ int avail)
{
struct rproc_mem_entry *mapping;
int ret;
@@ -508,6 +558,17 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
if (!rproc->domain)
return -EINVAL;
+ if (sizeof(*rsc) > avail) {
+ dev_err(rproc->dev, "devmem rsc is truncated\n");
+ return -EINVAL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved) {
+ dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
dev_err(rproc->dev, "kzalloc mapping failed\n");
@@ -531,7 +592,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
mapping->len = rsc->len;
list_add_tail(&mapping->node, &rproc->mappings);
- dev_dbg(rproc->dev, "mapped devmem pa 0x%llx, da 0x%llx, len 0x%x\n",
+ dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n",
rsc->pa, rsc->da, rsc->len);
return 0;
@@ -545,6 +606,7 @@ out:
* rproc_handle_carveout() - handle phys contig memory allocation requests
* @rproc: rproc handle
* @rsc: the resource entry
+ * @avail: size of available data (for image validation)
*
* This function will handle firmware requests for allocation of physically
* contiguous memory regions.
@@ -558,7 +620,8 @@ out:
* needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
* pressure is important; it may have a substantial impact on performance.
*/
-static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
+static int rproc_handle_carveout(struct rproc *rproc,
+ struct fw_rsc_carveout *rsc, int avail)
{
struct rproc_mem_entry *carveout, *mapping;
struct device *dev = rproc->dev;
@@ -566,6 +629,20 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
void *va;
int ret;
+ if (sizeof(*rsc) > avail) {
+ dev_err(rproc->dev, "carveout rsc is truncated\n");
+ return -EINVAL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved) {
+ dev_err(dev, "carveout rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n",
+ rsc->da, rsc->pa, rsc->len, rsc->flags);
+
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
dev_err(dev, "kzalloc mapping failed\n");
@@ -624,7 +701,7 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
mapping->len = rsc->len;
list_add_tail(&mapping->node, &rproc->mappings);
- dev_dbg(dev, "carveout mapped 0x%llx to 0x%x\n", rsc->da, dma);
+ dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma);
/*
* Some remote processors might need to know the pa
@@ -665,36 +742,44 @@ free_mapping:
* enum fw_resource_type.
*/
static rproc_handle_resource_t rproc_handle_rsc[] = {
- [RSC_CARVEOUT] = rproc_handle_carveout,
- [RSC_DEVMEM] = rproc_handle_devmem,
- [RSC_TRACE] = rproc_handle_trace,
- [RSC_VRING] = rproc_handle_vring,
- [RSC_VIRTIO_DEV] = NULL, /* handled early upon registration */
+ [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 */
};
/* handle firmware resource entries before booting the remote processor */
static int
-rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
+rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
{
struct device *dev = rproc->dev;
rproc_handle_resource_t handler;
- int ret = 0;
+ 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);
+ void *rsc = (void *)hdr + sizeof(*hdr);
+
+ /* make sure table isn't truncated */
+ if (avail < 0) {
+ dev_err(dev, "rsc table is truncated\n");
+ return -EINVAL;
+ }
- for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) {
- dev_dbg(dev, "rsc: type %d, da 0x%llx, pa 0x%llx, len 0x%x, "
- "id %d, name %s, flags %x\n", rsc->type, rsc->da,
- rsc->pa, rsc->len, rsc->id, rsc->name, rsc->flags);
+ dev_dbg(dev, "rsc: type %d\n", hdr->type);
- if (rsc->type >= RSC_LAST) {
- dev_warn(dev, "unsupported resource %d\n", rsc->type);
+ if (hdr->type >= RSC_LAST) {
+ dev_warn(dev, "unsupported resource %d\n", hdr->type);
continue;
}
- handler = rproc_handle_rsc[rsc->type];
+ handler = rproc_handle_rsc[hdr->type];
if (!handler)
continue;
- ret = handler(rproc, rsc);
+ ret = handler(rproc, rsc, avail);
if (ret)
break;
}
@@ -704,47 +789,64 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
/* handle firmware resource entries while registering the remote processor */
static int
-rproc_handle_virtio_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
+rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
{
struct device *dev = rproc->dev;
- int ret = -ENODEV;
+ 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;
- for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc))
- if (rsc->type == RSC_VIRTIO_DEV) {
- dev_dbg(dev, "found vdev %d/%s features %llx\n",
- rsc->flags, rsc->name, rsc->da);
- ret = rproc_handle_virtio_hdr(rproc, rsc);
+ ret = rproc_handle_vdev(rproc, vrsc, avail);
+ if (ret)
break;
- }
+ }
return ret;
}
/**
- * rproc_handle_resources() - find and handle the resource table
+ * rproc_find_rsc_table() - find the resource table
* @rproc: the rproc handle
* @elf_data: the content of the ELF firmware image
* @len: firmware size (in bytes)
- * @handler: function that should be used to handle the resource table
+ * @tablesz: place holder for providing back the table size
*
* This function finds the resource table inside the remote processor's
- * firmware, and invoke a user-supplied handler with it (we have two
- * possible handlers: one is invoked upon registration of @rproc,
- * in order to register the supported virito devices, and the other is
- * invoked when @rproc is actually booted).
- *
- * Currently this function fails if a resource table doesn't exist.
- * This restriction will be removed when we'll start supporting remote
- * processors that don't need a resource table.
+ * firmware. It is used both upon the registration of @rproc (in order
+ * to look for and register the supported virito devices), and when the
+ * @rproc is booted.
+ *
+ * Returns the pointer to the resource table if it is found, and write its
+ * size into @tablesz. If a valid table isn't found, NULL is returned
+ * (and @tablesz isn't set).
*/
-static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
- size_t len, rproc_handle_resources_t handler)
-
+static struct resource_table *
+rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len,
+ int *tablesz)
{
struct elf32_hdr *ehdr;
struct elf32_shdr *shdr;
const char *name_table;
- int i, ret = -EINVAL;
+ struct device *dev = rproc->dev;
+ struct resource_table *table = NULL;
+ int i;
ehdr = (struct elf32_hdr *)elf_data;
shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
@@ -752,24 +854,50 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
/* look for the resource table and handle it */
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
- if (!strcmp(name_table + shdr->sh_name, ".resource_table")) {
- struct fw_resource *table = (struct fw_resource *)
- (elf_data + shdr->sh_offset);
+ int size = shdr->sh_size;
+ int offset = shdr->sh_offset;
+
+ if (strcmp(name_table + shdr->sh_name, ".resource_table"))
+ continue;
- if (shdr->sh_offset + shdr->sh_size > len) {
- dev_err(rproc->dev,
- "truncated fw: need 0x%x avail 0x%x\n",
- shdr->sh_offset + shdr->sh_size, len);
- ret = -EINVAL;
- }
+ table = (struct resource_table *)(elf_data + offset);
- ret = handler(rproc, table, shdr->sh_size);
+ /* make sure we have the entire table */
+ if (offset + size > len) {
+ dev_err(dev, "resource table truncated\n");
+ return NULL;
+ }
- break;
+ /* make sure table has at least the header */
+ if (sizeof(struct resource_table) > size) {
+ dev_err(dev, "header-less resource table\n");
+ return NULL;
+ }
+
+ /* we don't support any version beyond the first */
+ if (table->ver != 1) {
+ dev_err(dev, "unsupported fw ver: %d\n", table->ver);
+ return NULL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (table->reserved[0] || table->reserved[1]) {
+ dev_err(dev, "non zero reserved bytes\n");
+ return NULL;
+ }
+
+ /* make sure the offsets array isn't truncated */
+ if (table->num * sizeof(table->offset[0]) +
+ sizeof(struct resource_table) > size) {
+ dev_err(dev, "resource table incomplete\n");
+ return NULL;
}
+
+ *tablesz = shdr->sh_size;
+ break;
}
- return ret;
+ return table;
}
/**
@@ -777,14 +905,12 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
* @rproc: rproc handle
*
* This function will free all resources acquired for @rproc, and it
- * is called when @rproc shuts down, or just failed booting.
+ * is called whenever @rproc either shuts down or fails to boot.
*/
static void rproc_resource_cleanup(struct rproc *rproc)
{
struct rproc_mem_entry *entry, *tmp;
struct device *dev = rproc->dev;
- struct rproc_vdev *rvdev = rproc->rvdev;
- int i;
/* clean up debugfs trace entries */
list_for_each_entry_safe(entry, tmp, &rproc->traces, node) {
@@ -794,23 +920,6 @@ static void rproc_resource_cleanup(struct rproc *rproc)
kfree(entry);
}
- /* free the coherent memory allocated for the vrings */
- for (i = 0; rvdev && i < ARRAY_SIZE(rvdev->vring); i++) {
- int qsz = rvdev->vring[i].len;
- void *va = rvdev->vring[i].va;
- int dma = rvdev->vring[i].dma;
-
- /* virtqueue size is expressed in number of buffers supported */
- if (qsz) {
- /* how many bytes does this vring really occupy ? */
- int size = PAGE_ALIGN(vring_size(qsz, AMP_VRING_ALIGN));
-
- dma_free_coherent(rproc->dev, size, va, dma);
-
- rvdev->vring[i].len = 0;
- }
- }
-
/* clean up carveout allocations */
list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
dma_free_coherent(dev, entry->len, entry->va, entry->dma);
@@ -840,6 +949,7 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
const char *name = rproc->firmware;
struct device *dev = rproc->dev;
struct elf32_hdr *ehdr;
+ char class;
if (!fw) {
dev_err(dev, "failed to load %s\n", name);