aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2012-03-07 15:21:12 -0800
committerOlof Johansson <olof@lixom.net>2012-03-07 15:21:12 -0800
commit6458acb5a31926dcc1295410221493544d628cf7 (patch)
tree15b5a855f73d52f95aeed2ab11e2e8028d82b746 /drivers
parentMerge tag 'rpmsg-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc into next/rpmsg (diff)
parentremoteproc: cleanup resource table parsing paths (diff)
downloadlinux-dev-6458acb5a31926dcc1295410221493544d628cf7.tar.xz
linux-dev-6458acb5a31926dcc1295410221493544d628cf7.zip
Merge tag 'rpmsg-fixes-and-more-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc into next/rpmsg
Fixing and cleaning up several remoteproc and rpmsg issues. In addition, remoteproc's resource table is converted to a collection of type-value members, instead of a rigid array of homogeneous structs. This enables remoteproc to support registration of generic virtio devices, and not only a single VIRTIO_ID_RPMSG virtio device. But perhaps more importantly, the resource table overhaul makes it possible to easily extend it in the future without breaking older images (simply by defining a new member type, while continuing to support older types). * tag 'rpmsg-fixes-and-more-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc: remoteproc: cleanup resource table parsing paths remoteproc: remove the hardcoded vring alignment remoteproc/omap: remove the mbox_callback limitation remoteproc: remove the single rpmsg vdev limitation remoteproc: safer boot/shutdown order remoteproc: remoteproc_rpmsg -> remoteproc_virtio remoteproc: resource table overhaul rpmsg: fix build warning when dma_addr_t is 64-bit rpmsg: fix published buffer length in rpmsg_recv_done rpmsg: validate incoming message length before propagating rpmsg: fix name service endpoint leak remoteproc/omap: two Kconfig fixes remoteproc: make sure we're parsing a 32bit firmware
Diffstat (limited to 'drivers')
-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
7 files changed, 453 insertions, 309 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 25fc4ccbc2ea..24d880e78ec6 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 df0897f69e16..5445d9b23294 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 aa3ce52dc65e..69425c4e86f3 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 729911b67a9a..ee15c68fb519 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);
@@ -853,6 +963,13 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
ehdr = (struct elf32_hdr *)fw->data;
+ /* We only support ELF32 at this point */
+ class = ehdr->e_ident[EI_CLASS];
+ if (class != ELFCLASS32) {
+ dev_err(dev, "Unsupported class: %d\n", class);
+ return -EINVAL;
+ }
+
/* We assume the firmware has the same endianess as the host */
# ifdef __LITTLE_ENDIAN
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
@@ -894,7 +1011,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
struct device *dev = rproc->dev;
const char *name = rproc->firmware;
struct elf32_hdr *ehdr;
- int ret;
+ struct resource_table *table;
+ int ret, tablesz;
ret = rproc_fw_sanity_check(rproc, fw);
if (ret)
@@ -921,9 +1039,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
*/
rproc->bootaddr = ehdr->e_entry;
+ /* look for the resource table */
+ table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz);
+ if (!table)
+ goto clean_up;
+
/* handle fw resources which are required to boot rproc */
- ret = rproc_handle_resources(rproc, fw->data, fw->size,
- rproc_handle_boot_rsc);
+ ret = rproc_handle_boot_rsc(rproc, table, tablesz);
if (ret) {
dev_err(dev, "Failed to process resources: %d\n", ret);
goto clean_up;
@@ -966,22 +1088,19 @@ clean_up:
static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
{
struct rproc *rproc = context;
- struct device *dev = rproc->dev;
- int ret;
+ struct resource_table *table;
+ int ret, tablesz;
if (rproc_fw_sanity_check(rproc, fw) < 0)
goto out;
- /* does the fw supports any virtio devices ? */
- ret = rproc_handle_resources(rproc, fw->data, fw->size,
- rproc_handle_virtio_rsc);
- if (ret) {
- dev_info(dev, "No fw virtio device was found\n");
+ /* look for the resource table */
+ table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz);
+ if (!table)
goto out;
- }
- /* add the virtio device (currently only rpmsg vdevs are supported) */
- ret = rproc_add_rpmsg_vdev(rproc);
+ /* look for virtio devices and register them */
+ ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
if (ret)
goto out;
@@ -1146,13 +1265,23 @@ EXPORT_SYMBOL(rproc_shutdown);
void rproc_release(struct kref *kref)
{
struct rproc *rproc = container_of(kref, struct rproc, refcount);
+ struct rproc_vdev *rvdev, *rvtmp;
dev_info(rproc->dev, "removing %s\n", rproc->name);
rproc_delete_debug_dir(rproc);
- /* at this point no one holds a reference to rproc anymore */
- kfree(rproc);
+ /* clean up remote vdev entries */
+ list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) {
+ __rproc_free_vrings(rvdev, RVDEV_NUM_VRINGS);
+ list_del(&rvdev->node);
+ }
+
+ /*
+ * At this point no one holds a reference to rproc anymore,
+ * so we can directly unroll rproc_alloc()
+ */
+ rproc_free(rproc);
}
/* will be called when an rproc is added to the rprocs klist */
@@ -1196,7 +1325,7 @@ static struct rproc *next_rproc(struct klist_iter *i)
* use rproc_put() to decrement it back once rproc isn't needed anymore.
*
* Note: currently this function (and its counterpart rproc_put()) are not
- * used anymore by the rpmsg subsystem. We need to scrutinize the use cases
+ * being used. We need to scrutinize the use cases
* that still need them, and see if we can migrate them to use the non
* name-based boot/shutdown interface.
*/
@@ -1271,11 +1400,8 @@ EXPORT_SYMBOL(rproc_put);
* firmware.
*
* If found, those virtio devices will be created and added, so as a result
- * of registering this remote processor, additional virtio drivers will be
+ * of registering this remote processor, additional virtio drivers might be
* probed.
- *
- * Currently, though, we only support a single RPMSG virtio vdev per remote
- * processor.
*/
int rproc_register(struct rproc *rproc)
{
@@ -1298,7 +1424,7 @@ int rproc_register(struct rproc *rproc)
/*
* We must retrieve early virtio configuration info from
- * the firmware (e.g. whether to register a virtio rpmsg device,
+ * the firmware (e.g. whether to register a virtio device,
* what virtio features does it support, ...).
*
* We're initiating an asynchronous firmware loading, so we can
@@ -1367,9 +1493,12 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
mutex_init(&rproc->lock);
+ idr_init(&rproc->notifyids);
+
INIT_LIST_HEAD(&rproc->carveouts);
INIT_LIST_HEAD(&rproc->mappings);
INIT_LIST_HEAD(&rproc->traces);
+ INIT_LIST_HEAD(&rproc->rvdevs);
rproc->state = RPROC_OFFLINE;
@@ -1389,6 +1518,9 @@ EXPORT_SYMBOL(rproc_alloc);
*/
void rproc_free(struct rproc *rproc)
{
+ idr_remove_all(&rproc->notifyids);
+ idr_destroy(&rproc->notifyids);
+
kfree(rproc);
}
EXPORT_SYMBOL(rproc_free);
@@ -1415,18 +1547,22 @@ EXPORT_SYMBOL(rproc_free);
*/
int rproc_unregister(struct rproc *rproc)
{
+ struct rproc_vdev *rvdev;
+
if (!rproc)
return -EINVAL;
/* if rproc is just being registered, wait */
wait_for_completion(&rproc->firmware_loading_complete);
- /* was an rpmsg vdev created ? */
- if (rproc->rvdev)
- rproc_remove_rpmsg_vdev(rproc);
+ /* clean up remote vdev entries */
+ list_for_each_entry(rvdev, &rproc->rvdevs, node)
+ rproc_remove_virtio_dev(rvdev);
- klist_remove(&rproc->node);
+ /* the rproc is downref'ed as soon as it's removed from the klist */
+ klist_del(&rproc->node);
+ /* the rproc will only be released after its refcount drops to zero */
kref_put(&rproc->refcount, rproc_release);
return 0;
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 8b2fc40e92d0..9f336d6bdef3 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -28,9 +28,9 @@ struct rproc;
void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
-/* from remoteproc_rpmsg.c */
-int rproc_add_rpmsg_vdev(struct rproc *);
-void rproc_remove_rpmsg_vdev(struct rproc *rproc);
+/* from remoteproc_virtio.c */
+int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
+void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
/* from remoteproc_debugfs.c */
void rproc_remove_trace_file(struct dentry *tfile);
diff --git a/drivers/remoteproc/remoteproc_rpmsg.c b/drivers/remoteproc/remoteproc_virtio.c
index 4f73e811bb80..ecf612130750 100644
--- a/drivers/remoteproc/remoteproc_rpmsg.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -19,7 +19,6 @@
#include <linux/export.h>
#include <linux/remoteproc.h>
-#include <linux/rpmsg.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
#include <linux/virtio_ids.h>
@@ -30,45 +29,41 @@
#include "remoteproc_internal.h"
-/**
- * struct rproc_virtio_vq_info - virtqueue state
- * @vq_id: a unique index of this virtqueue (unique for this @rproc)
- * @rproc: handle to the remote processor
- *
- * Such a struct will be maintained for every virtqueue we're
- * using to communicate with the remote processor
- */
-struct rproc_virtio_vq_info {
- __u16 vq_id;
- struct rproc *rproc;
-};
-
/* kick the remote processor, and let it know which virtqueue to poke at */
static void rproc_virtio_notify(struct virtqueue *vq)
{
- struct rproc_virtio_vq_info *rpvq = vq->priv;
- struct rproc *rproc = rpvq->rproc;
+ struct rproc_vring *rvring = vq->priv;
+ struct rproc *rproc = rvring->rvdev->rproc;
+ int notifyid = rvring->notifyid;
- dev_dbg(rproc->dev, "kicking vq id: %d\n", rpvq->vq_id);
+ dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid);
- rproc->ops->kick(rproc, rpvq->vq_id);
+ rproc->ops->kick(rproc, notifyid);
}
/**
* rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
* @rproc: handle to the remote processor
- * @vq_id: index of the signalled virtqueue
+ * @notifyid: index of the signalled virtqueue (unique per this @rproc)
*
* This function should be called by the platform-specific rproc driver,
* when the remote processor signals that a specific virtqueue has pending
* messages available.
*
- * Returns IRQ_NONE if no message was found in the @vq_id virtqueue,
+ * Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
* and otherwise returns IRQ_HANDLED.
*/
-irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id)
+irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
{
- return vring_interrupt(0, rproc->rvdev->vq[vq_id]);
+ struct rproc_vring *rvring;
+
+ dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid);
+
+ rvring = idr_find(&rproc->notifyids, notifyid);
+ if (!rvring || !rvring->vq)
+ return IRQ_NONE;
+
+ return vring_interrupt(0, rvring->vq);
}
EXPORT_SYMBOL(rproc_vq_interrupt);
@@ -77,60 +72,60 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
void (*callback)(struct virtqueue *vq),
const char *name)
{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev);
- struct rproc_vdev *rvdev = rproc->rvdev;
- struct rproc_virtio_vq_info *rpvq;
+ struct rproc_vring *rvring;
struct virtqueue *vq;
void *addr;
- int ret, len;
+ int len, size;
- rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL);
- if (!rpvq)
- return ERR_PTR(-ENOMEM);
+ /* we're temporarily limited to two virtqueues per rvdev */
+ if (id >= ARRAY_SIZE(rvdev->vring))
+ return ERR_PTR(-EINVAL);
+
+ rvring = &rvdev->vring[id];
- rpvq->rproc = rproc;
- rpvq->vq_id = id;
+ addr = rvring->va;
+ len = rvring->len;
- addr = rvdev->vring[id].va;
- len = rvdev->vring[id].len;
+ /* zero vring */
+ size = vring_size(len, rvring->align);
+ memset(addr, 0, size);
- dev_dbg(rproc->dev, "vring%d: va %p qsz %d\n", id, addr, len);
+ dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n",
+ id, addr, len, rvring->notifyid);
/*
* Create the new vq, and tell virtio we're not interested in
* the 'weak' smp barriers, since we're talking with a real device.
*/
- vq = vring_new_virtqueue(len, AMP_VRING_ALIGN, vdev, false, addr,
+ vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr,
rproc_virtio_notify, callback, name);
if (!vq) {
dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name);
- ret = -ENOMEM;
- goto free_rpvq;
+ return ERR_PTR(-ENOMEM);
}
- rvdev->vq[id] = vq;
- vq->priv = rpvq;
+ rvring->vq = vq;
+ vq->priv = rvring;
return vq;
-
-free_rpvq:
- kfree(rpvq);
- return ERR_PTR(ret);
}
static void rproc_virtio_del_vqs(struct virtio_device *vdev)
{
struct virtqueue *vq, *n;
struct rproc *rproc = vdev_to_rproc(vdev);
+ struct rproc_vring *rvring;
+
+ /* power down the remote processor before deleting vqs */
+ rproc_shutdown(rproc);
list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
- struct rproc_virtio_vq_info *rpvq = vq->priv;
+ rvring = vq->priv;
+ rvring->vq = NULL;
vring_del_virtqueue(vq);
- kfree(rpvq);
}
-
- /* power down the remote processor */
- rproc_shutdown(rproc);
}
static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
@@ -141,17 +136,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
struct rproc *rproc = vdev_to_rproc(vdev);
int i, ret;
- /* we maintain two virtqueues per remote processor (for RX and TX) */
- if (nvqs != 2)
- return -EINVAL;
-
- /* boot the remote processor */
- ret = rproc_boot(rproc);
- if (ret) {
- dev_err(rproc->dev, "rproc_boot() failed %d\n", ret);
- goto error;
- }
-
for (i = 0; i < nvqs; ++i) {
vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
if (IS_ERR(vqs[i])) {
@@ -160,6 +144,13 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
}
}
+ /* now that the vqs are all set, boot the remote processor */
+ ret = rproc_boot(rproc);
+ if (ret) {
+ dev_err(rproc->dev, "rproc_boot() failed %d\n", ret);
+ goto error;
+ }
+
return 0;
error:
@@ -170,7 +161,7 @@ error:
/*
* We don't support yet real virtio status semantics.
*
- * The plan is to provide this via the VIRTIO HDR resource entry
+ * The plan is to provide this via the VDEV resource entry
* which is part of the firmware: this way the remote processor
* will be able to access the status values as set by us.
*/
@@ -181,7 +172,7 @@ static u8 rproc_virtio_get_status(struct virtio_device *vdev)
static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
{
- dev_dbg(&vdev->dev, "new status: %d\n", status);
+ dev_dbg(&vdev->dev, "status: %d\n", status);
}
static void rproc_virtio_reset(struct virtio_device *vdev)
@@ -192,15 +183,14 @@ static void rproc_virtio_reset(struct virtio_device *vdev)
/* provide the vdev features as retrieved from the firmware */
static u32 rproc_virtio_get_features(struct virtio_device *vdev)
{
- struct rproc *rproc = vdev_to_rproc(vdev);
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- /* we only support a single vdev device for now */
- return rproc->rvdev->dfeatures;
+ return rvdev->dfeatures;
}
static void rproc_virtio_finalize_features(struct virtio_device *vdev)
{
- struct rproc *rproc = vdev_to_rproc(vdev);
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
/* Give virtio_ring a chance to accept features */
vring_transport_features(vdev);
@@ -214,7 +204,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev)
* fixed as part of a small resource table overhaul and then an
* extension of the virtio resource entries.
*/
- rproc->rvdev->gfeatures = vdev->features[0];
+ rvdev->gfeatures = vdev->features[0];
}
static struct virtio_config_ops rproc_virtio_config_ops = {
@@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev)
}
/**
- * rproc_add_rpmsg_vdev() - create an rpmsg virtio device
- * @rproc: the rproc handle
+ * rproc_add_virtio_dev() - register an rproc-induced virtio device
+ * @rvdev: the remote vdev
*
- * This function is called if virtio rpmsg support was found in the
- * firmware of the remote processor.
+ * This function registers a virtio device. This vdev's partent is
+ * the rproc device.
*
- * Today we only support creating a single rpmsg vdev (virtio device),
- * but the plan is to remove this limitation. At that point this interface
- * will be revised/extended.
+ * Returns 0 on success or an appropriate error value otherwise.
*/
-int rproc_add_rpmsg_vdev(struct rproc *rproc)
+int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
{
+ struct rproc *rproc = rvdev->rproc;
struct device *dev = rproc->dev;
- struct rproc_vdev *rvdev = rproc->rvdev;
+ struct virtio_device *vdev = &rvdev->vdev;
int ret;
- rvdev->vdev.id.device = VIRTIO_ID_RPMSG,
- rvdev->vdev.config = &rproc_virtio_config_ops,
- rvdev->vdev.dev.parent = dev;
- rvdev->vdev.dev.release = rproc_vdev_release;
+ vdev->id.device = id,
+ vdev->config = &rproc_virtio_config_ops,
+ vdev->dev.parent = dev;
+ vdev->dev.release = rproc_vdev_release;
/*
* We're indirectly making a non-temporary copy of the rproc pointer
@@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc)
*/
kref_get(&rproc->refcount);
- ret = register_virtio_device(&rvdev->vdev);
+ ret = register_virtio_device(vdev);
if (ret) {
kref_put(&rproc->refcount, rproc_release);
dev_err(dev, "failed to register vdev: %d\n", ret);
+ goto out;
}
+ dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
+
+out:
return ret;
}
/**
- * rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device
- * @rproc: the rproc handle
+ * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
+ * @rvdev: the remote vdev
*
- * This function is called whenever @rproc is removed _iff_ an rpmsg
- * vdev was created beforehand.
+ * This function unregisters an existing virtio device.
*/
-void rproc_remove_rpmsg_vdev(struct rproc *rproc)
+void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
{
- struct rproc_vdev *rvdev = rproc->rvdev;
-
unregister_virtio_device(&rvdev->vdev);
}
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 8980ac2cc546..75506ec2840e 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -290,22 +290,36 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
EXPORT_SYMBOL(rpmsg_create_ept);
/**
- * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
+ * __rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
+ * @vrp: virtproc which owns this ept
* @ept: endpoing to destroy
*
- * Should be used by drivers to destroy an rpmsg endpoint previously
- * created with rpmsg_create_ept().
+ * An internal function which destroy an ept without assuming it is
+ * bound to an rpmsg channel. This is needed for handling the internal
+ * name service endpoint, which isn't bound to an rpmsg channel.
+ * See also __rpmsg_create_ept().
*/
-void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
+static void
+__rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept)
{
- struct virtproc_info *vrp = ept->rpdev->vrp;
-
mutex_lock(&vrp->endpoints_lock);
idr_remove(&vrp->endpoints, ept->addr);
mutex_unlock(&vrp->endpoints_lock);
kfree(ept);
}
+
+/**
+ * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
+ * @ept: endpoing to destroy
+ *
+ * Should be used by drivers to destroy an rpmsg endpoint previously
+ * created with rpmsg_create_ept().
+ */
+void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
+{
+ __rpmsg_destroy_ept(ept->rpdev->vrp, ept);
+}
EXPORT_SYMBOL(rpmsg_destroy_ept);
/*
@@ -764,6 +778,16 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1,
msg, sizeof(*msg) + msg->len, true);
+ /*
+ * We currently use fixed-sized buffers, so trivially sanitize
+ * the reported payload length.
+ */
+ if (len > RPMSG_BUF_SIZE ||
+ msg->len > (len - sizeof(struct rpmsg_hdr))) {
+ dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len);
+ return;
+ }
+
/* use the dst addr to fetch the callback of the appropriate user */
mutex_lock(&vrp->endpoints_lock);
ept = idr_find(&vrp->endpoints, msg->dst);
@@ -774,7 +798,8 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
else
dev_warn(dev, "msg received with no recepient\n");
- sg_init_one(&sg, msg, sizeof(*msg) + len);
+ /* publish the real size of the buffer */
+ sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
/* add the buffer back to the remote processor's virtqueue */
err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
@@ -891,8 +916,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
if (!bufs_va)
goto vqs_del;
- dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%x\n", bufs_va,
- vrp->bufs_dma);
+ dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%llx\n", bufs_va,
+ (unsigned long long)vrp->bufs_dma);
/* half of the buffers is dedicated for RX */
vrp->rbufs = bufs_va;
@@ -964,6 +989,9 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev)
if (ret)
dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret);
+ if (vrp->ns_ept)
+ __rpmsg_destroy_ept(vrp, vrp->ns_ept);
+
idr_remove_all(&vrp->endpoints);
idr_destroy(&vrp->endpoints);