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);
} }
......
This diff is collapsed.
...@@ -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);
......
This diff is collapsed.
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