Commit 6458acb5 authored by Olof Johansson's avatar Olof Johansson

Merge tag 'rpmsg-fixes-and-more-for-3.4' of...

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
parents ab646a24 1e3e2c7c
...@@ -20,6 +20,11 @@ platform-specific remoteproc drivers only need to provide a few low-level ...@@ -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 handlers, and then all rpmsg drivers will then just work
(for more information about the virtio-based rpmsg bus and its drivers, (for more information about the virtio-based rpmsg bus and its drivers,
please read Documentation/rpmsg.txt). 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 2. User API
...@@ -136,8 +141,6 @@ int dummy_rproc_example(struct rproc *my_rproc) ...@@ -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 If found, those virtio devices will be created and added, so as a result
of registering this remote processor, additional virtio drivers might get of registering this remote processor, additional virtio drivers might get
probed. probed.
Currently, though, we only support a single RPMSG virtio vdev per remote
processor.
int rproc_unregister(struct rproc *rproc) int rproc_unregister(struct rproc *rproc)
- Unregister a remote processor, and decrement its refcount. - Unregister a remote processor, and decrement its refcount.
...@@ -174,7 +177,7 @@ struct rproc_ops { ...@@ -174,7 +177,7 @@ struct rproc_ops {
}; };
Every remoteproc implementation should at least provide the ->start and ->stop 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. should be provided as well.
The ->start() handler takes an rproc handle and should then power on the 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 ...@@ -221,43 +224,52 @@ resource entries that publish the existence of supported features
or configurations by the remote processor, such as trace buffers and or configurations by the remote processor, such as trace buffers and
supported virtio devices (and their configurations). 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 * @type: resource type
* @id: index number of the resource * @data: resource data
* @da: device address of the resource *
* @pa: physical address of the resource * Every resource entry begins with a 'struct fw_rsc_hdr' header providing
* @len: size, in bytes, of the resource * its @type. The content of the entry itself will immediately follow
* @flags: properties of the resource, e.g. iommu protection required * this header, and it should be parsed according to the resource type.
* @reserved: must be 0 atm
* @name: name of resource
*/ */
struct fw_resource { struct fw_rsc_hdr {
u32 type; u32 type;
u32 id; u8 data[0];
u64 da;
u64 pa;
u32 len;
u32 flags;
u8 reserved[16];
u8 name[48];
} __packed; } __packed;
Some resources entries are mere announcements, where the host is informed Some resources entries are mere announcements, where the host is informed
of specific remoteproc configuration. Other entries require the host to of specific remoteproc configuration. Other entries require the host to
do something (e.g. reserve a requested resource) and possibly also reply do something (e.g. allocate a system resource). Sometimes a negotiation
by overwriting a member inside 'struct fw_resource' with info about the is expected, where the firmware requests a resource, and once allocated,
allocated resource. the host should provide back its details (e.g. address of an allocated
memory region).
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.
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 * enum fw_resource_type - types of resource entries
...@@ -266,59 +278,45 @@ Here are the resource types that are currently being used: ...@@ -266,59 +278,45 @@ Here are the resource types that are currently being used:
* memory region. * memory region.
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral. * @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
* @RSC_TRACE: announces the availability of a trace buffer into which * @RSC_TRACE: announces the availability of a trace buffer into which
* the remote processor will be writing logs. In this case, * the remote processor will be writing logs.
* 'da' indicates the device address where logs are written to, * @RSC_VDEV: declare support for a virtio device, and serve as its
* and 'len' is the size of the trace buffer. * virtio header.
* @RSC_VRING: request for allocation of a virtio vring (address should * @RSC_LAST: just keep this one at the end
* be indicated in 'da', and 'len' should contain the number *
* of buffers supported by the vring). * Please note that these values are used as indices to the rproc_handle_rsc
* @RSC_VIRTIO_DEV: announces support for a virtio device, and serves as * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
* the virtio header. 'da' contains the virtio device * check the validity of an index before the lookup table is accessed, so
* features, 'pa' holds the virtio guest features (host * please update it as needed.
* 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).
*/ */
enum fw_resource_type { enum fw_resource_type {
RSC_CARVEOUT = 0, RSC_CARVEOUT = 0,
RSC_DEVMEM = 1, RSC_DEVMEM = 1,
RSC_TRACE = 2, RSC_TRACE = 2,
RSC_VRING = 3, RSC_VDEV = 3,
RSC_VIRTIO_DEV = 4, RSC_LAST = 4,
RSC_VIRTIO_CFG = 5,
}; };
Most of the resource entries share the basic idea of address/length For more details regarding a specific resource type, please see its
negotiation with the host: the firmware usually asks for memory dedicated structure in include/linux/remoteproc.h.
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).
We also expect that platform-specific resource entries will show up 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. type, and hand those resources to the platform-specific rproc driver to handle.
7. Virtio and remoteproc 7. Virtio and remoteproc
The firmware should provide remoteproc information about virtio devices The firmware should provide remoteproc information about virtio devices
that it supports, and their configurations: a RSC_VIRTIO_DEV resource entry that it supports, and their configurations: a RSC_VDEV resource entry
should specify the virtio device id, and subsequent RSC_VRING resource entries should specify the virtio device id (as in virtio_ids.h), virtio features,
should indicate the vring size (i.e. how many buffers do they support) and virtio config space, vrings information, etc.
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. When a new remote processor is registered, the remoteproc framework
will look for its resource table and will register the virtio devices
At this point we only support a single virtio rpmsg device per remote it supports. A firmware may support any number of virtio devices, and
processor, but the plan is to remove this limitation. In addition, once we of any type (a single remote processor can also easily support several
move to TLV-based resource table, the plan is to have a single RSC_VIRTIO rpmsg virtio devices this way, if desired).
entry per supported virtio device, which will include the virtio header,
the vrings information and the virtio config space. Of course, RSC_VDEV resource entries are only good enough for static
Of course, RSC_VIRTIO resource entries are only good enough for static
allocation of virtio devices. Dynamic allocations will also be made possible allocation of virtio devices. Dynamic allocations will also be made possible
using the rpmsg bus (similar to how we already do dynamic allocations of using the rpmsg bus (similar to how we already do dynamic allocations of
rpmsg channels; read more about it in rpmsg.txt). rpmsg channels; read more about it in rpmsg.txt).
...@@ -8,11 +8,10 @@ config REMOTEPROC ...@@ -8,11 +8,10 @@ config REMOTEPROC
config OMAP_REMOTEPROC config OMAP_REMOTEPROC
tristate "OMAP remoteproc support" tristate "OMAP remoteproc support"
depends on ARCH_OMAP4 depends on ARCH_OMAP4
select OMAP_IOMMU depends on OMAP_IOMMU
select REMOTEPROC select REMOTEPROC
select OMAP_MBOX_FWK select OMAP_MBOX_FWK
select RPMSG select RPMSG
default m
help help
Say y here to support OMAP's remote processors (dual M3 Say y here to support OMAP's remote processors (dual M3
and DSP on OMAP4) via the remote processor framework. and DSP on OMAP4) via the remote processor framework.
......
...@@ -5,5 +5,5 @@ ...@@ -5,5 +5,5 @@
obj-$(CONFIG_REMOTEPROC) += remoteproc.o obj-$(CONFIG_REMOTEPROC) += remoteproc.o
remoteproc-y := remoteproc_core.o remoteproc-y := remoteproc_core.o
remoteproc-y += remoteproc_debugfs.o remoteproc-y += remoteproc_debugfs.o
remoteproc-y += remoteproc_rpmsg.o remoteproc-y += remoteproc_virtio.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
...@@ -80,16 +80,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this, ...@@ -80,16 +80,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
dev_info(dev, "received echo reply from %s\n", name); dev_info(dev, "received echo reply from %s\n", name);
break; break;
default: default:
/* ignore vq indices which are too large to be valid */ /* msg contains the index of the triggered vring */
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.
*/
if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE) if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
dev_dbg(dev, "no message was found in vqid %d\n", msg); dev_dbg(dev, "no message was found in vqid %d\n", msg);
} }
......
...@@ -52,8 +52,8 @@ static void klist_rproc_put(struct klist_node *n); ...@@ -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 * We need this in order to support name-based lookups (needed by the
* rproc_get_by_name()). * rproc_get_by_name()).
* *
* That said, we don't use rproc_get_by_name() anymore within the rpmsg * That said, we don't use rproc_get_by_name() at this point.
* framework. The use cases that do require its existence should be * The use cases that do require its existence should be
* scrutinized, and hopefully migrated to rproc_boot() using device-based * scrutinized, and hopefully migrated to rproc_boot() using device-based
* binding. * binding.
* *
...@@ -63,9 +63,8 @@ static void klist_rproc_put(struct klist_node *n); ...@@ -63,9 +63,8 @@ static void klist_rproc_put(struct klist_node *n);
static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put);
typedef int (*rproc_handle_resources_t)(struct rproc *rproc, typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
struct fw_resource *rsc, int len); struct resource_table *table, int len);
typedef int (*rproc_handle_resource_t)(struct rproc *rproc, typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
struct fw_resource *rsc);
/* /*
* This is the IOMMU fault handler we register with the IOMMU API * 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) ...@@ -280,145 +279,182 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
return ret; return ret;
} }
/** static int
* rproc_handle_virtio_hdr() - handle a virtio header resource __rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
* @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)
{ {
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 */ /* make sure reserved bytes are zeroes */
if (rsc->flags != VIRTIO_ID_RPMSG) { if (vring->reserved) {
dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->flags); dev_err(dev, "vring rsc has non zero reserved bytes\n");
return -EINVAL; return -EINVAL;
} }
/* we only support a single vdev per rproc for now */ /* verify queue size and vring alignment are sane */
if (rsc->id || rproc->rvdev) { if (!vring->num || !vring->align) {
dev_warn(rproc->dev, "redundant vdev entry: %s\n", rsc->name); dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
vring->num, vring->align);
return -EINVAL; return -EINVAL;
} }
rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); /* actual size of vring (in bytes) */
if (!rvdev) 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; 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; /* assign an rproc-wide unique index for this vring */
rvdev->rproc = rproc; /* 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; 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 * @rproc: the remote processor
* @rsc: the vring resource descriptor * @rsc: the vring resource descriptor
* * @avail: size of available data (for sanity checking the image)
* This resource entry requires allocation of non-cacheable memory *
* for a virtio vring. Currently we only support two vrings per remote * This resource entry requests the host to statically register a virtio
* processor, required for the virtio rpmsg device. * device (vdev), and setup everything needed to support it. It contains
* * everything needed to make it possible: the virtio device id, virtio
* The 'len' member of @rsc should contain the number of buffers this vring * device features, vrings information, virtio config space, etc...
* support and 'da' should either contain the device address where *
* the remote processor is expecting the vring, or indicate that * Before registering the vdev, the vrings are allocated from non-cacheable
* dynamically allocation of the vring's device address is supported. * physically contiguous memory. Currently we only support two vrings per
* * remote processor (temporary limitation). We might also want to consider
* Note: 'da' is currently not handled. This will be revised when the generic * doing the vring allocation only later when ->find_vqs() is invoked, and
* iommu-based DMA API will arrive, or a dynanic & non-iommu use case show * then release them upon ->del_vqs().
* up. Meanwhile, statically-addressed iommu-based images should use *
* RSC_DEVMEM resource entries to map their require 'da' to the physical * Note: @da is currently not really handled correctly: we dynamically
* address of their base CMA region. * 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 * 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 device *dev = rproc->dev;
struct rproc_vdev *rvdev = rproc->rvdev; struct rproc_vdev *rvdev;
dma_addr_t dma; int i, ret;
int size, id = rsc->id;
void *va;
/* no vdev is in place ? */ /* make sure resource isn't truncated */
if (!rvdev) { if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring)
dev_err(dev, "vring requested without a virtio dev entry\n"); + rsc->config_len > avail) {
dev_err(rproc->dev, "vdev rsc is truncated\n");
return -EINVAL; return -EINVAL;
} }
/* the firmware must provide the expected queue size */ /* make sure reserved bytes are zeroes */
if (!rsc->len) { if (rsc->reserved[0] || rsc->reserved[1]) {
dev_err(dev, "missing expected queue size\n"); dev_err(dev, "vdev rsc has non zero reserved bytes\n");
return -EINVAL; return -EINVAL;
} }
/* we currently support two vrings per rproc (for rx and tx) */ dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n",
if (id >= ARRAY_SIZE(rvdev->vring)) { rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);
dev_err(dev, "%s: invalid vring id %d\n", rsc->name, id);
return -EINVAL;
}
/* have we already allocated this vring id ? */ /* we currently support only two vrings per rvdev */
if (rvdev->vring[id].len) { if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) {
dev_err(dev, "%s: duplicated id %d\n", rsc->name, id); dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings);
return -EINVAL; return -EINVAL;
} }
/* actual size of vring (in bytes) */ rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
size = PAGE_ALIGN(vring_size(rsc->len, AMP_VRING_ALIGN)); if (!rvdev)
/*
* 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 -ENOMEM; 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, /* remember the device features */
dma, rsc->len, size); rvdev->dfeatures = rsc->dfeatures;
list_add_tail(&rvdev->node, &rproc->rvdevs);
rvdev->vring[id].len = rsc->len; /* it is now safe to add the virtio device */
rvdev->vring[id].va = va; ret = rproc_add_virtio_dev(rvdev, rsc->id);
rvdev->vring[id].dma = dma; if (ret)
goto free_vrings;
return 0; return 0;
free_vrings:
__rproc_free_vrings(rvdev, i);
kfree(rvdev);
return ret;
} }
/** /**
* rproc_handle_trace() - handle a shared trace buffer resource * rproc_handle_trace() - handle a shared trace buffer resource
* @rproc: the remote processor * @rproc: the remote processor
* @rsc: the trace resource descriptor * @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, * In case the remote processor dumps trace logs into memory,
* export it via debugfs. * export it via debugfs.
...@@ -430,13 +466,25 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc) ...@@ -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 * 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 rproc_mem_entry *trace;
struct device *dev = rproc->dev; struct device *dev = rproc->dev;
void *ptr; void *ptr;
char name[15]; 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 ? */ /* what's the kernel address of this resource ? */
ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
if (!ptr) { if (!ptr) {
...@@ -469,7 +517,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) ...@@ -469,7 +517,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
rproc->num_traces++; 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); rsc->da, rsc->len);
return 0; return 0;
...@@ -479,6 +527,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) ...@@ -479,6 +527,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
* rproc_handle_devmem() - handle devmem resource entry * rproc_handle_devmem() - handle devmem resource entry
* @rproc: remote processor handle * @rproc: remote processor handle
* @rsc: the devmem resource entry * @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. * 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) ...@@ -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 * and not allow firmwares to request access to physical addresses that
* are outside those ranges. * 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; struct rproc_mem_entry *mapping;
int ret; int ret;
...@@ -508,6 +558,17 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) ...@@ -508,6 +558,17 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
if (!rproc->domain) if (!rproc->domain)
return -EINVAL; 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); mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) { if (!mapping) {
dev_err(rproc->dev, "kzalloc mapping failed\n"); dev_err(rproc->dev, "kzalloc mapping failed\n");
...@@ -531,7 +592,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) ...@@ -531,7 +592,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
mapping->len = rsc->len; mapping->len = rsc->len;
list_add_tail(&mapping->node, &rproc->mappings); 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); rsc->pa, rsc->da, rsc->len);
return 0; return 0;
...@@ -545,6 +606,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) ...@@ -545,6 +606,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
* rproc_handle_carveout() - handle phys contig memory allocation requests * rproc_handle_carveout() - handle phys contig memory allocation requests
* @rproc: rproc handle * @rproc: rproc handle
* @rsc: the resource entry * @rsc: the resource entry
* @avail: size of available data (for image validation)
* *
* This function will handle firmware requests for allocation of physically * This function will handle firmware requests for allocation of physically
* contiguous memory regions. * contiguous memory regions.
...@@ -558,7 +620,8 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) ...@@ -558,7 +620,8 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
* needed to map it (in case @rproc is using an IOMMU). Reducing the TLB * 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. * 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 rproc_mem_entry *carveout, *mapping;
struct device *dev = rproc->dev; struct device *dev = rproc->dev;
...@@ -566,6 +629,20 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) ...@@ -566,6 +629,20 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
void *va; void *va;
int ret; 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); mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) { if (!mapping) {
dev_err(dev, "kzalloc mapping failed\n"); dev_err(dev, "kzalloc mapping failed\n");
...@@ -624,7 +701,7 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) ...@@ -624,7 +701,7 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
mapping->len = rsc->len; mapping->len = rsc->len;
list_add_tail(&mapping->node, &rproc->mappings); 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 * Some remote processors might need to know the pa
...@@ -665,36 +742,44 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) ...@@ -665,36 +742,44 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
* enum fw_resource_type. * enum fw_resource_type.
*/ */
static rproc_handle_resource_t rproc_handle_rsc[] = { static rproc_handle_resource_t rproc_handle_rsc[] = {
[RSC_CARVEOUT] = rproc_handle_carveout, [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
[RSC_DEVMEM] = rproc_handle_devmem, [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
[RSC_TRACE] = rproc_handle_trace, [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
[RSC_VRING] = rproc_handle_vring, [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
[RSC_VIRTIO_DEV] = NULL, /* handled early upon registration */
}; };
/* handle firmware resource entries before booting the remote processor */ /* handle firmware resource entries before booting the remote processor */
static int 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; struct device *dev = rproc->dev;
rproc_handle_resource_t handler; 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\n", hdr->type);
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);
if (rsc->type >= RSC_LAST) { if (hdr->type >= RSC_LAST) {
dev_warn(dev, "unsupported resource %d\n", rsc->type); dev_warn(dev, "unsupported resource %d\n", hdr->type);
continue; continue;
} }
handler = rproc_handle_rsc[rsc->type]; handler = rproc_handle_rsc[hdr->type];
if (!handler) if (!handler)
continue; continue;
ret = handler(rproc, rsc); ret = handler(rproc, rsc, avail);
if (ret) if (ret)
break; break;
} }
...@@ -704,47 +789,64 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) ...@@ -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 */ /* handle firmware resource entries while registering the remote processor */
static int 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; 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)) ret = rproc_handle_vdev(rproc, vrsc, avail);
if (rsc->type == RSC_VIRTIO_DEV) { if (ret)
dev_dbg(dev, "found vdev %d/%s features %llx\n",
rsc->flags, rsc->name, rsc->da);
ret = rproc_handle_virtio_hdr(rproc, rsc);
break; break;
} }
return ret; return ret;
} }
/** /**
* rproc_handle_resources() - find and handle the resource table * rproc_find_rsc_table() - find the resource table
* @rproc: the rproc handle * @rproc: the rproc handle
* @elf_data: the content of the ELF firmware image * @elf_data: the content of the ELF firmware image
* @len: firmware size (in bytes) * @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 * This function finds the resource table inside the remote processor's
* firmware, and invoke a user-supplied handler with it (we have two * firmware. It is used both upon the registration of @rproc (in order
* possible handlers: one is invoked upon registration of @rproc, * to look for and register the supported virito devices), and when the
* in order to register the supported virito devices, and the other is * @rproc is booted.
* invoked when @rproc is actually booted). *
* * Returns the pointer to the resource table if it is found, and write its
* Currently this function fails if a resource table doesn't exist. * size into @tablesz. If a valid table isn't found, NULL is returned
* This restriction will be removed when we'll start supporting remote * (and @tablesz isn't set).
* processors that don't need a resource table.
*/ */
static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data, static struct resource_table *
size_t len, rproc_handle_resources_t handler) rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len,
int *tablesz)
{ {
struct elf32_hdr *ehdr; struct elf32_hdr *ehdr;
struct elf32_shdr *shdr; struct elf32_shdr *shdr;
const char *name_table; 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; ehdr = (struct elf32_hdr *)elf_data;
shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); 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, ...@@ -752,24 +854,50 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
/* look for the resource table and handle it */ /* look for the resource table and handle it */
for (i = 0; i < ehdr->e_shnum; i++, shdr++) { for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
if (!strcmp(name_table + shdr->sh_name, ".resource_table")) { int size = shdr->sh_size;
struct fw_resource *table = (struct fw_resource *) int offset = shdr->sh_offset;
(elf_data + shdr->sh_offset);
if (strcmp(name_table + shdr->sh_name, ".resource_table"))
continue;
if (shdr->sh_offset + shdr->sh_size > len) { table = (struct resource_table *)(elf_data + offset);
dev_err(rproc->dev,
"truncated fw: need 0x%x avail 0x%x\n",
shdr->sh_offset + shdr->sh_size, len);
ret = -EINVAL;
}
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, ...@@ -777,14 +905,12 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
* @rproc: rproc handle * @rproc: rproc handle
* *
* This function will free all resources acquired for @rproc, and it * 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) static void rproc_resource_cleanup(struct rproc *rproc)
{ {
struct rproc_mem_entry *entry, *tmp; struct rproc_mem_entry *entry, *tmp;
struct device *dev = rproc->dev; struct device *dev = rproc->dev;
struct rproc_vdev *rvdev = rproc->rvdev;
int i;
/* clean up debugfs trace entries */ /* clean up debugfs trace entries */
list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { list_for_each_entry_safe(entry, tmp, &rproc->traces, node) {
...@@ -794,23 +920,6 @@ static void rproc_resource_cleanup(struct rproc *rproc) ...@@ -794,23 +920,6 @@ static void rproc_resource_cleanup(struct rproc *rproc)
kfree(entry); 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 */ /* clean up carveout allocations */
list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
dma_free_coherent(dev, entry->len, entry->va, entry->dma); 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) ...@@ -840,6 +949,7 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
const char *name = rproc->firmware; const char *name = rproc->firmware;
struct device *dev = rproc->dev; struct device *dev = rproc->dev;
struct elf32_hdr *ehdr; struct elf32_hdr *ehdr;
char class;
if (!fw) { if (!fw) {
dev_err(dev, "failed to load %s\n", name); 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) ...@@ -853,6 +963,13 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
ehdr = (struct elf32_hdr *)fw->data; 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 */ /* We assume the firmware has the same endianess as the host */
# ifdef __LITTLE_ENDIAN # ifdef __LITTLE_ENDIAN
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
...@@ -894,7 +1011,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) ...@@ -894,7 +1011,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
struct device *dev = rproc->dev; struct device *dev = rproc->dev;
const char *name = rproc->firmware; const char *name = rproc->firmware;
struct elf32_hdr *ehdr; struct elf32_hdr *ehdr;
int ret; struct resource_table *table;
int ret, tablesz;
ret = rproc_fw_sanity_check(rproc, fw); ret = rproc_fw_sanity_check(rproc, fw);
if (ret) if (ret)
...@@ -921,9 +1039,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) ...@@ -921,9 +1039,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
*/ */
rproc->bootaddr = ehdr->e_entry; 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 */ /* handle fw resources which are required to boot rproc */
ret = rproc_handle_resources(rproc, fw->data, fw->size, ret = rproc_handle_boot_rsc(rproc, table, tablesz);
rproc_handle_boot_rsc);
if (ret) { if (ret) {
dev_err(dev, "Failed to process resources: %d\n", ret); dev_err(dev, "Failed to process resources: %d\n", ret);
goto clean_up; goto clean_up;
...@@ -966,22 +1088,19 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) ...@@ -966,22 +1088,19 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
static void rproc_fw_config_virtio(const struct firmware *fw, void *context) static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
{ {
struct rproc *rproc = context; struct rproc *rproc = context;
struct device *dev = rproc->dev; struct resource_table *table;
int ret; int ret, tablesz;
if (rproc_fw_sanity_check(rproc, fw) < 0) if (rproc_fw_sanity_check(rproc, fw) < 0)
goto out; goto out;
/* does the fw supports any virtio devices ? */ /* look for the resource table */
ret = rproc_handle_resources(rproc, fw->data, fw->size, table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz);
rproc_handle_virtio_rsc); if (!table)
if (ret) {
dev_info(dev, "No fw virtio device was found\n");
goto out; goto out;
}
/* add the virtio device (currently only rpmsg vdevs are supported) */ /* look for virtio devices and register them */
ret = rproc_add_rpmsg_vdev(rproc); ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
if (ret) if (ret)
goto out; goto out;
...@@ -1146,13 +1265,23 @@ EXPORT_SYMBOL(rproc_shutdown); ...@@ -1146,13 +1265,23 @@ EXPORT_SYMBOL(rproc_shutdown);
void rproc_release(struct kref *kref) void rproc_release(struct kref *kref)
{ {
struct rproc *rproc = container_of(kref, struct rproc, refcount); struct rproc *rproc = container_of(kref, struct rproc, refcount);
struct rproc_vdev *rvdev, *rvtmp;
dev_info(rproc->dev, "removing %s\n", rproc->name); dev_info(rproc->dev, "removing %s\n", rproc->name);
rproc_delete_debug_dir(rproc); rproc_delete_debug_dir(rproc);
/* at this point no one holds a reference to rproc anymore */ /* clean up remote vdev entries */
kfree(rproc); 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 */ /* 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) ...@@ -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. * use rproc_put() to decrement it back once rproc isn't needed anymore.
* *
* Note: currently this function (and its counterpart rproc_put()) are not * 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 * that still need them, and see if we can migrate them to use the non
* name-based boot/shutdown interface. * name-based boot/shutdown interface.
*/ */
...@@ -1271,11 +1400,8 @@ EXPORT_SYMBOL(rproc_put); ...@@ -1271,11 +1400,8 @@ EXPORT_SYMBOL(rproc_put);
* firmware. * firmware.
* *
* If found, those virtio devices will be created and added, so as a result * 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. * probed.
*
* Currently, though, we only support a single RPMSG virtio vdev per remote
* processor.
*/ */
int rproc_register(struct rproc *rproc) int rproc_register(struct rproc *rproc)
{ {
...@@ -1298,7 +1424,7 @@ int rproc_register(struct rproc *rproc) ...@@ -1298,7 +1424,7 @@ int rproc_register(struct rproc *rproc)
/* /*
* We must retrieve early virtio configuration info from * 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, ...). * what virtio features does it support, ...).
* *
* We're initiating an asynchronous firmware loading, so we can * We're initiating an asynchronous firmware loading, so we can
...@@ -1367,9 +1493,12 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, ...@@ -1367,9 +1493,12 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
mutex_init(&rproc->lock); mutex_init(&rproc->lock);
idr_init(&rproc->notifyids);
INIT_LIST_HEAD(&rproc->carveouts); INIT_LIST_HEAD(&rproc->carveouts);
INIT_LIST_HEAD(&rproc->mappings); INIT_LIST_HEAD(&rproc->mappings);
INIT_LIST_HEAD(&rproc->traces); INIT_LIST_HEAD(&rproc->traces);
INIT_LIST_HEAD(&rproc->rvdevs);
rproc->state = RPROC_OFFLINE; rproc->state = RPROC_OFFLINE;
...@@ -1389,6 +1518,9 @@ EXPORT_SYMBOL(rproc_alloc); ...@@ -1389,6 +1518,9 @@ EXPORT_SYMBOL(rproc_alloc);
*/ */
void rproc_free(struct rproc *rproc) void rproc_free(struct rproc *rproc)
{ {
idr_remove_all(&rproc->notifyids);
idr_destroy(&rproc->notifyids);
kfree(rproc); kfree(rproc);
} }
EXPORT_SYMBOL(rproc_free); EXPORT_SYMBOL(rproc_free);
...@@ -1415,18 +1547,22 @@ EXPORT_SYMBOL(rproc_free); ...@@ -1415,18 +1547,22 @@ EXPORT_SYMBOL(rproc_free);
*/ */
int rproc_unregister(struct rproc *rproc) int rproc_unregister(struct rproc *rproc)
{ {
struct rproc_vdev *rvdev;
if (!rproc) if (!rproc)
return -EINVAL; return -EINVAL;
/* if rproc is just being registered, wait */ /* if rproc is just being registered, wait */
wait_for_completion(&rproc->firmware_loading_complete); wait_for_completion(&rproc->firmware_loading_complete);
/* was an rpmsg vdev created ? */ /* clean up remote vdev entries */
if (rproc->rvdev) list_for_each_entry(rvdev, &rproc->rvdevs, node)
rproc_remove_rpmsg_vdev(rproc); 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); kref_put(&rproc->refcount, rproc_release);
return 0; return 0;
......
...@@ -28,9 +28,9 @@ struct rproc; ...@@ -28,9 +28,9 @@ struct rproc;
void rproc_release(struct kref *kref); void rproc_release(struct kref *kref);
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
/* from remoteproc_rpmsg.c */ /* from remoteproc_virtio.c */
int rproc_add_rpmsg_vdev(struct rproc *); int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
void rproc_remove_rpmsg_vdev(struct rproc *rproc); void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
/* from remoteproc_debugfs.c */ /* from remoteproc_debugfs.c */
void rproc_remove_trace_file(struct dentry *tfile); void rproc_remove_trace_file(struct dentry *tfile);
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/remoteproc.h> #include <linux/remoteproc.h>
#include <linux/rpmsg.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_config.h> #include <linux/virtio_config.h>
#include <linux/virtio_ids.h> #include <linux/virtio_ids.h>
...@@ -30,45 +29,41 @@ ...@@ -30,45 +29,41 @@
#include "remoteproc_internal.h" #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 */ /* kick the remote processor, and let it know which virtqueue to poke at */
static void rproc_virtio_notify(struct virtqueue *vq) static void rproc_virtio_notify(struct virtqueue *vq)
{ {
struct rproc_virtio_vq_info *rpvq = vq->priv; struct rproc_vring *rvring = vq->priv;
struct rproc *rproc = rpvq->rproc; 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_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
* @rproc: handle to the remote processor * @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, * This function should be called by the platform-specific rproc driver,
* when the remote processor signals that a specific virtqueue has pending * when the remote processor signals that a specific virtqueue has pending
* messages available. * 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. * 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); EXPORT_SYMBOL(rproc_vq_interrupt);
...@@ -77,60 +72,60 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, ...@@ -77,60 +72,60 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
void (*callback)(struct virtqueue *vq), void (*callback)(struct virtqueue *vq),
const char *name) const char *name)
{ {
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev); struct rproc *rproc = vdev_to_rproc(vdev);
struct rproc_vdev *rvdev = rproc->rvdev; struct rproc_vring *rvring;
struct rproc_virtio_vq_info *rpvq;
struct virtqueue *vq; struct virtqueue *vq;
void *addr; void *addr;
int ret, len; int len, size;
rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL); /* we're temporarily limited to two virtqueues per rvdev */
if (!rpvq) if (id >= ARRAY_SIZE(rvdev->vring))
return ERR_PTR(-ENOMEM); return ERR_PTR(-EINVAL);
rvring = &rvdev->vring[id];
rpvq->rproc = rproc; addr = rvring->va;
rpvq->vq_id = id; len = rvring->len;
addr = rvdev->vring[id].va; /* zero vring */
len = rvdev->vring[id].len; 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 * Create the new vq, and tell virtio we're not interested in
* the 'weak' smp barriers, since we're talking with a real device. * 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); rproc_virtio_notify, callback, name);
if (!vq) { if (!vq) {
dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name); dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name);
ret = -ENOMEM; return ERR_PTR(-ENOMEM);
goto free_rpvq;
} }
rvdev->vq[id] = vq; rvring->vq = vq;
vq->priv = rpvq; vq->priv = rvring;
return vq; return vq;
free_rpvq:
kfree(rpvq);
return ERR_PTR(ret);
} }
static void rproc_virtio_del_vqs(struct virtio_device *vdev) static void rproc_virtio_del_vqs(struct virtio_device *vdev)
{ {
struct virtqueue *vq, *n; struct virtqueue *vq, *n;
struct rproc *rproc = vdev_to_rproc(vdev); 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) { 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); 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, 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, ...@@ -141,17 +136,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
struct rproc *rproc = vdev_to_rproc(vdev); struct rproc *rproc = vdev_to_rproc(vdev);
int i, ret; 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) { for (i = 0; i < nvqs; ++i) {
vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]); vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
if (IS_ERR(vqs[i])) { if (IS_ERR(vqs[i])) {
...@@ -160,6 +144,13 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, ...@@ -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; return 0;
error: error:
...@@ -170,7 +161,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, ...@@ -170,7 +161,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
/* /*
* We don't support yet real virtio status semantics. * 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 * which is part of the firmware: this way the remote processor
* will be able to access the status values as set by us. * 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) ...@@ -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) 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) static void rproc_virtio_reset(struct virtio_device *vdev)
...@@ -192,15 +183,14 @@ 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 */ /* provide the vdev features as retrieved from the firmware */
static u32 rproc_virtio_get_features(struct virtio_device *vdev) 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 rvdev->dfeatures;
return rproc->rvdev->dfeatures;
} }
static void rproc_virtio_finalize_features(struct virtio_device *vdev) 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 */ /* Give virtio_ring a chance to accept features */
vring_transport_features(vdev); vring_transport_features(vdev);
...@@ -214,7 +204,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *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 * fixed as part of a small resource table overhaul and then an
* extension of the virtio resource entries. * 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 = { static struct virtio_config_ops rproc_virtio_config_ops = {
...@@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev) ...@@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev)
} }
/** /**
* rproc_add_rpmsg_vdev() - create an rpmsg virtio device * rproc_add_virtio_dev() - register an rproc-induced virtio device
* @rproc: the rproc handle * @rvdev: the remote vdev
* *
* This function is called if virtio rpmsg support was found in the * This function registers a virtio device. This vdev's partent is
* firmware of the remote processor. * the rproc device.
* *
* Today we only support creating a single rpmsg vdev (virtio device), * Returns 0 on success or an appropriate error value otherwise.
* but the plan is to remove this limitation. At that point this interface
* will be revised/extended.
*/ */
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 device *dev = rproc->dev;
struct rproc_vdev *rvdev = rproc->rvdev; struct virtio_device *vdev = &rvdev->vdev;
int ret; int ret;
rvdev->vdev.id.device = VIRTIO_ID_RPMSG, vdev->id.device = id,
rvdev->vdev.config = &rproc_virtio_config_ops, vdev->config = &rproc_virtio_config_ops,
rvdev->vdev.dev.parent = dev; vdev->dev.parent = dev;
rvdev->vdev.dev.release = rproc_vdev_release; vdev->dev.release = rproc_vdev_release;
/* /*
* We're indirectly making a non-temporary copy of the rproc pointer * We're indirectly making a non-temporary copy of the rproc pointer
...@@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc) ...@@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc)
*/ */
kref_get(&rproc->refcount); kref_get(&rproc->refcount);
ret = register_virtio_device(&rvdev->vdev); ret = register_virtio_device(vdev);
if (ret) { if (ret) {
kref_put(&rproc->refcount, rproc_release); kref_put(&rproc->refcount, rproc_release);
dev_err(dev, "failed to register vdev: %d\n", ret); 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; return ret;
} }
/** /**
* rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
* @rproc: the rproc handle * @rvdev: the remote vdev
* *
* This function is called whenever @rproc is removed _iff_ an rpmsg * This function unregisters an existing virtio device.
* vdev was created beforehand.
*/ */
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); unregister_virtio_device(&rvdev->vdev);
} }
...@@ -290,22 +290,36 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, ...@@ -290,22 +290,36 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
EXPORT_SYMBOL(rpmsg_create_ept); 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 * @ept: endpoing to destroy
* *
* Should be used by drivers to destroy an rpmsg endpoint previously * An internal function which destroy an ept without assuming it is
* created with rpmsg_create_ept(). * 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); mutex_lock(&vrp->endpoints_lock);
idr_remove(&vrp->endpoints, ept->addr); idr_remove(&vrp->endpoints, ept->addr);
mutex_unlock(&vrp->endpoints_lock); mutex_unlock(&vrp->endpoints_lock);
kfree(ept); 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); EXPORT_SYMBOL(rpmsg_destroy_ept);
/* /*
...@@ -764,6 +778,16 @@ static void rpmsg_recv_done(struct virtqueue *rvq) ...@@ -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, print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1,
msg, sizeof(*msg) + msg->len, true); 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 */ /* use the dst addr to fetch the callback of the appropriate user */
mutex_lock(&vrp->endpoints_lock); mutex_lock(&vrp->endpoints_lock);
ept = idr_find(&vrp->endpoints, msg->dst); ept = idr_find(&vrp->endpoints, msg->dst);
...@@ -774,7 +798,8 @@ static void rpmsg_recv_done(struct virtqueue *rvq) ...@@ -774,7 +798,8 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
else else
dev_warn(dev, "msg received with no recepient\n"); 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 */ /* add the buffer back to the remote processor's virtqueue */
err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL); err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
...@@ -891,8 +916,8 @@ static int rpmsg_probe(struct virtio_device *vdev) ...@@ -891,8 +916,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
if (!bufs_va) if (!bufs_va)
goto vqs_del; goto vqs_del;
dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%x\n", bufs_va, dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%llx\n", bufs_va,
vrp->bufs_dma); (unsigned long long)vrp->bufs_dma);
/* half of the buffers is dedicated for RX */ /* half of the buffers is dedicated for RX */
vrp->rbufs = bufs_va; vrp->rbufs = bufs_va;
...@@ -964,6 +989,9 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev) ...@@ -964,6 +989,9 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev)
if (ret) if (ret)
dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", 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_remove_all(&vrp->endpoints);
idr_destroy(&vrp->endpoints); idr_destroy(&vrp->endpoints);
......
...@@ -41,48 +41,54 @@ ...@@ -41,48 +41,54 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/idr.h>
/*
* The alignment between the consumer and producer parts of the vring.
* Note: this is part of the "wire" protocol. If you change this, you need
* to update your peers too.
*/
#define AMP_VRING_ALIGN (4096)
/** /**
* struct fw_resource - describes an entry from the resource section * struct resource_table - firmware resource table header
* @type: resource type * @ver: version number
* @id: index number of the resource * @num: number of resource entries
* @da: device address of the resource * @reserved: reserved (must be zero)
* @pa: physical address of the resource * @offset: array of offsets pointing at the various resource entries
* @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
* *
* The remote processor firmware should contain a "resource table": * A resource table is essentially a list of system resources required
* array of 'struct fw_resource' entries. * by the remote processor. It may also include configuration entries.
* If needed, the remote processor firmware should contain this table
* as a dedicated ".resource_table" ELF section.
* *
* Some resources entries are mere announcements, where the host is informed * Some resources entries are mere announcements, where the host is informed
* of specific remoteproc configuration. Other entries require the host to * of specific remoteproc configuration. Other entries require the host to
* do something (e.g. reserve a requested resource) and possibly also reply * do something (e.g. allocate a system resource). Sometimes a negotiation
* by overwriting a member inside 'struct fw_resource' with info about the * is expected, where the firmware requests a resource, and once allocated,
* allocated resource. * the host should provide back its details (e.g. address of an allocated
* * memory region).
* Different resource entries use different members of this struct, *
* with different meanings. This is pretty limiting and error-prone, * The header of the resource table, as expressed by this structure,
* so the plan is to move to variable-length TLV-based resource entries, * contains a version number (should we need to change this format in the
* where each resource type will have its own structure. * future), the number of available resource entries, and their offsets
* in the table.
*
* Immediately following this header are the resource entries themselves,
* each of which begins with a resource entry header (as described below).
*/ */
struct fw_resource { struct resource_table {
u32 ver;
u32 num;
u32 reserved[2];
u32 offset[0];
} __packed;
/**
* struct fw_rsc_hdr - firmware resource entry header
* @type: resource type
* @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_rsc_hdr {
u32 type; u32 type;
u32 id; u8 data[0];
u64 da;
u64 pa;
u32 len;
u32 flags;
u8 reserved[16];
u8 name[48];
} __packed; } __packed;
/** /**
...@@ -92,30 +98,13 @@ struct fw_resource { ...@@ -92,30 +98,13 @@ struct fw_resource {
* memory region. * memory region.
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral. * @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
* @RSC_TRACE: announces the availability of a trace buffer into which * @RSC_TRACE: announces the availability of a trace buffer into which
* the remote processor will be writing logs. In this case, * the remote processor will be writing logs.
* 'da' indicates the device address where logs are written to, * @RSC_VDEV: declare support for a virtio device, and serve as its
* and 'len' is the size of the trace buffer. * virtio header.
* @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: this entry declares about support for a virtio device,
* and serves as the virtio header. 'da' holds the
* the virtio device features, 'pa' holds the virtio guest
* features, 'len' holds the virtio status, and 'flags' holds
* the virtio id (currently only VIRTIO_ID_RPMSG is supported).
* @RSC_LAST: just keep this one at the end * @RSC_LAST: just keep this one at the end
* *
* Most of the resource entries share the basic idea of address/length * For more details regarding a specific resource type, please see its
* negotiation with the host: the firmware usually asks (on behalf of the * dedicated structure below.
* remote processor that will soon be booted with it) 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).
* *
* Please note that these values are used as indices to the rproc_handle_rsc * 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 * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
...@@ -126,11 +115,197 @@ enum fw_resource_type { ...@@ -126,11 +115,197 @@ enum fw_resource_type {
RSC_CARVEOUT = 0, RSC_CARVEOUT = 0,
RSC_DEVMEM = 1, RSC_DEVMEM = 1,
RSC_TRACE = 2, RSC_TRACE = 2,
RSC_VRING = 3, RSC_VDEV = 3,
RSC_VIRTIO_DEV = 4, RSC_LAST = 4,
RSC_LAST = 5,
}; };
#define FW_RSC_ADDR_ANY (0xFFFFFFFFFFFFFFFF)
/**
* struct fw_rsc_carveout - physically contiguous memory request
* @da: device address
* @pa: physical address
* @len: length (in bytes)
* @flags: iommu protection flags
* @reserved: reserved (must be zero)
* @name: human-readable name of the requested memory region
*
* This resource entry requests the host to allocate a physically contiguous
* memory region.
*
* These request entries should precede other firmware resource entries,
* as other entries might request placing other data objects inside
* these memory regions (e.g. data/code segments, trace resource entries, ...).
*
* Allocating memory this way helps utilizing the reserved physical memory
* (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
* 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.
*
* If the firmware is compiled with static addresses, then @da should specify
* the expected device address of this memory region. If @da is set to
* FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then
* overwrite @da with the dynamically allocated address.
*
* We will always use @da to negotiate the device addresses, even if it
* isn't using an iommu. In that case, though, it will obviously contain
* physical addresses.
*
* Some remote processors needs to know the allocated physical address
* even if they do use an iommu. This is needed, e.g., if they control
* hardware accelerators which access the physical memory directly (this
* is the case with OMAP4 for instance). In that case, the host will
* overwrite @pa with the dynamically allocated physical address.
* Generally we don't want to expose physical addresses if we don't have to
* (remote processors are generally _not_ trusted), so we might want to
* change this to happen _only_ when explicitly required by the hardware.
*
* @flags is used to provide IOMMU protection flags, and @name should
* (optionally) contain a human readable name of this carveout region
* (mainly for debugging purposes).
*/
struct fw_rsc_carveout {
u32 da;
u32 pa;
u32 len;
u32 flags;
u32 reserved;
u8 name[32];
} __packed;
/**
* struct fw_rsc_devmem - iommu mapping request
* @da: device address
* @pa: physical address
* @len: length (in bytes)
* @flags: iommu protection flags
* @reserved: reserved (must be zero)
* @name: human-readable name of the requested region to be mapped
*
* This resource entry requests the host to iommu map a physically contiguous
* memory region. This is needed in case the remote processor requires
* access to certain memory-based peripherals; _never_ use it to access
* regular memory.
*
* This is obviously only needed if the remote processor is accessing memory
* via an iommu.
*
* @da should specify the required device address, @pa should specify
* the physical address we want to map, @len should specify the size of
* the mapping and @flags is the IOMMU protection flags. As always, @name may
* (optionally) contain a human readable name of this mapping (mainly for
* debugging purposes).
*
* Note: at this point we just "trust" those devmem entries to contain valid
* physical addresses, but this isn't safe and will be changed: eventually we
* want remoteproc implementations to provide us ranges of physical addresses
* the firmware is allowed to request, and not allow firmwares to request
* access to physical addresses that are outside those ranges.
*/
struct fw_rsc_devmem {
u32 da;
u32 pa;
u32 len;
u32 flags;
u32 reserved;
u8 name[32];
} __packed;
/**
* struct fw_rsc_trace - trace buffer declaration
* @da: device address
* @len: length (in bytes)
* @reserved: reserved (must be zero)
* @name: human-readable name of the trace buffer
*
* This resource entry provides the host information about a trace buffer
* into which the remote processor will write log messages.
*
* @da specifies the device address of the buffer, @len specifies
* its size, and @name may contain a human readable name of the trace buffer.
*
* After booting the remote processor, the trace buffers are exposed to the
* user via debugfs entries (called trace0, trace1, etc..).
*/
struct fw_rsc_trace {
u32 da;
u32 len;
u32 reserved;
u8 name[32];
} __packed;
/**
* struct fw_rsc_vdev_vring - vring descriptor entry
* @da: device address
* @align: the alignment between the consumer and producer parts of the vring
* @num: num of buffers supported by this vring (must be power of two)
* @notifyid is a unique rproc-wide notify index for this vring. This notify
* index is used when kicking a remote processor, to let it know that this
* vring is triggered.
* @reserved: reserved (must be zero)
*
* This descriptor is not a resource entry by itself; it is part of the
* vdev resource type (see below).
*
* Note that @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.
*/
struct fw_rsc_vdev_vring {
u32 da;
u32 align;
u32 num;
u32 notifyid;
u32 reserved;
} __packed;
/**
* struct fw_rsc_vdev - virtio device header
* @id: virtio device id (as in virtio_ids.h)
* @notifyid is a unique rproc-wide notify index for this vdev. This notify
* index is used when kicking a remote processor, to let it know that the
* status/features of this vdev have changes.
* @dfeatures specifies the virtio device features supported by the firmware
* @gfeatures is a place holder used by the host to write back the
* negotiated features that are supported by both sides.
* @config_len is the size of the virtio config space of this vdev. The config
* space lies in the resource table immediate after this vdev header.
* @status is a place holder where the host will indicate its virtio progress.
* @num_of_vrings indicates how many vrings are described in this vdev header
* @reserved: reserved (must be zero)
* @vring is an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'.
*
* This resource is a virtio device header: it provides information about
* the vdev, and is then used by the host and its peer remote processors
* to negotiate and share certain virtio properties.
*
* By providing this resource entry, the firmware essentially asks remoteproc
* to statically allocate a vdev upon registration of the rproc (dynamic vdev
* allocation is not yet supported).
*
* Note: unlike virtualization systems, the term 'host' here means
* the Linux side which is running remoteproc to control the remote
* processors. We use the name 'gfeatures' to comply with virtio's terms,
* though there isn't really any virtualized guest OS here: it's the host
* which is responsible for negotiating the final features.
* Yeah, it's a bit confusing.
*
* Note: immediately following this structure is the virtio config space for
* this vdev (which is specific to the vdev; for more info, read the virtio
* spec). the size of the config space is specified by @config_len.
*/
struct fw_rsc_vdev {
u32 id;
u32 notifyid;
u32 dfeatures;
u32 gfeatures;
u32 config_len;
u8 status;
u8 num_of_vrings;
u8 reserved[2];
struct fw_rsc_vdev_vring vring[0];
} __packed;
/** /**
* struct rproc_mem_entry - memory entry descriptor * struct rproc_mem_entry - memory entry descriptor
* @va: virtual address * @va: virtual address
...@@ -144,7 +319,7 @@ struct rproc_mem_entry { ...@@ -144,7 +319,7 @@ struct rproc_mem_entry {
void *va; void *va;
dma_addr_t dma; dma_addr_t dma;
int len; int len;
u64 da; u32 da;
void *priv; void *priv;
struct list_head node; struct list_head node;
}; };
...@@ -206,7 +381,8 @@ enum rproc_state { ...@@ -206,7 +381,8 @@ enum rproc_state {
* @mappings: list of iommu mappings we initiated, needed on shutdown * @mappings: list of iommu mappings we initiated, needed on shutdown
* @firmware_loading_complete: marks e/o asynchronous firmware loading * @firmware_loading_complete: marks e/o asynchronous firmware loading
* @bootaddr: address of first instruction to boot rproc with (optional) * @bootaddr: address of first instruction to boot rproc with (optional)
* @rvdev: virtio device (we only support a single rpmsg virtio device for now) * @rvdevs: list of remote virtio devices
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids
*/ */
struct rproc { struct rproc {
struct klist_node node; struct klist_node node;
...@@ -226,24 +402,50 @@ struct rproc { ...@@ -226,24 +402,50 @@ struct rproc {
struct list_head carveouts; struct list_head carveouts;
struct list_head mappings; struct list_head mappings;
struct completion firmware_loading_complete; struct completion firmware_loading_complete;
u64 bootaddr; u32 bootaddr;
struct list_head rvdevs;
struct idr notifyids;
};
/* we currently support only two vrings per rvdev */
#define RVDEV_NUM_VRINGS 2
/**
* struct rproc_vring - remoteproc vring state
* @va: virtual address
* @dma: dma address
* @len: length, in bytes
* @da: device address
* @align: vring alignment
* @notifyid: rproc-specific unique vring index
* @rvdev: remote vdev
* @vq: the virtqueue of this vring
*/
struct rproc_vring {
void *va;
dma_addr_t dma;
int len;
u32 da;
u32 align;
int notifyid;
struct rproc_vdev *rvdev; struct rproc_vdev *rvdev;
struct virtqueue *vq;
}; };
/** /**
* struct rproc_vdev - remoteproc state for a supported virtio device * struct rproc_vdev - remoteproc state for a supported virtio device
* @node: list node
* @rproc: the rproc handle * @rproc: the rproc handle
* @vdev: the virio device * @vdev: the virio device
* @vq: the virtqueues for this vdev
* @vring: the vrings for this vdev * @vring: the vrings for this vdev
* @dfeatures: virtio device features * @dfeatures: virtio device features
* @gfeatures: virtio guest features * @gfeatures: virtio guest features
*/ */
struct rproc_vdev { struct rproc_vdev {
struct list_head node;
struct rproc *rproc; struct rproc *rproc;
struct virtio_device vdev; struct virtio_device vdev;
struct virtqueue *vq[2]; struct rproc_vring vring[RVDEV_NUM_VRINGS];
struct rproc_mem_entry vring[2];
unsigned long dfeatures; unsigned long dfeatures;
unsigned long gfeatures; unsigned long gfeatures;
}; };
...@@ -261,9 +463,14 @@ int rproc_unregister(struct rproc *rproc); ...@@ -261,9 +463,14 @@ int rproc_unregister(struct rproc *rproc);
int rproc_boot(struct rproc *rproc); int rproc_boot(struct rproc *rproc);
void rproc_shutdown(struct rproc *rproc); void rproc_shutdown(struct rproc *rproc);
static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev)
{
return container_of(vdev, struct rproc_vdev, vdev);
}
static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev)
{ {
struct rproc_vdev *rvdev = container_of(vdev, struct rproc_vdev, vdev); struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
return rvdev->rproc; return rvdev->rproc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment