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). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJPVk4nAAoJELLolMlTRIoMKgwP/Rz79D9SYD6in8vI4mCPRfvb UFHqV7yFTaQeeV9E+u05VYI4Eaty1CcmVHWKxklqj+hixImsOssNkcd2o++E8Fms DJuEgDpT1q6IuljjWc9HSTdhup3/MnWsFaXz4fKeQ7f6CZGV5GLaHMA45LhGpb9T IiPXyOfwPcNnUzgmfYktfteY30PdJ4iPQTrU3ILf7Uvjb65RFvV2heu43f/E/UjP ywymaegFxK1IsQ2Wq3DH1zY2jJKS4Et4iJMiaS+mwkfijwEasTzLcaeGWW9wZZQD nRgmyIGbMB1UynW63y6THSptJ0+V6jym+TUqZmsgYh3Jyprmv3EKSTqo0lQJ13c9 HbT8B8SRU0PBRs2ljdli+2nHaO1KkBkL2ny1xNMPNIjVfzaI6HuApbHxVUvoOT9g Ayj8BZm1dUiU/cPDq7WI44zpKURinOcIDwNYh+CW6TrjxPQ7quIQt5p9fm3T1q8S IoolnDI258iDtaHWEqdhQwGaonBSllvFC1gawTmQ21pRY3q7EadhZLa24Q3kiEoh cLW+OHW7cof42cqWJWgbPJhsS1SyiipXrURXhiF/Kp2URRXW5cvNaQXliskIMLmB CtyRyHFn39Jo+VIt6HpQDxQQLXC33mBeHTPp9N7IbCFGzMr3Q20g7JDgxfsvO23w LuLb0tFZ+By3nwJs2UEs =NFNd -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
6458acb5a3
|
@ -20,6 +20,11 @@ platform-specific remoteproc drivers only need to provide a few low-level
|
|||
handlers, and then all rpmsg drivers will then just work
|
||||
(for more information about the virtio-based rpmsg bus and its drivers,
|
||||
please read Documentation/rpmsg.txt).
|
||||
Registration of other types of virtio devices is now also possible. Firmwares
|
||||
just need to publish what kind of virtio devices do they support, and then
|
||||
remoteproc will add those devices. This makes it possible to reuse the
|
||||
existing virtio drivers with remote processor backends at a minimal development
|
||||
cost.
|
||||
|
||||
2. User API
|
||||
|
||||
|
@ -136,8 +141,6 @@ int dummy_rproc_example(struct rproc *my_rproc)
|
|||
If found, those virtio devices will be created and added, so as a result
|
||||
of registering this remote processor, additional virtio drivers might get
|
||||
probed.
|
||||
Currently, though, we only support a single RPMSG virtio vdev per remote
|
||||
processor.
|
||||
|
||||
int rproc_unregister(struct rproc *rproc)
|
||||
- Unregister a remote processor, and decrement its refcount.
|
||||
|
@ -174,7 +177,7 @@ struct rproc_ops {
|
|||
};
|
||||
|
||||
Every remoteproc implementation should at least provide the ->start and ->stop
|
||||
handlers. If rpmsg functionality is also desired, then the ->kick handler
|
||||
handlers. If rpmsg/virtio functionality is also desired, then the ->kick handler
|
||||
should be provided as well.
|
||||
|
||||
The ->start() handler takes an rproc handle and should then power on the
|
||||
|
@ -221,43 +224,52 @@ resource entries that publish the existence of supported features
|
|||
or configurations by the remote processor, such as trace buffers and
|
||||
supported virtio devices (and their configurations).
|
||||
|
||||
Currently the resource table is just an array of:
|
||||
The resource table begins with this header:
|
||||
|
||||
/**
|
||||
* struct fw_resource - describes an entry from the resource section
|
||||
* @type: resource type
|
||||
* @id: index number of the resource
|
||||
* @da: device address of the resource
|
||||
* @pa: physical address of the resource
|
||||
* @len: size, in bytes, of the resource
|
||||
* @flags: properties of the resource, e.g. iommu protection required
|
||||
* @reserved: must be 0 atm
|
||||
* @name: name of resource
|
||||
* 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 fw_resource {
|
||||
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
|
||||
* @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 id;
|
||||
u64 da;
|
||||
u64 pa;
|
||||
u32 len;
|
||||
u32 flags;
|
||||
u8 reserved[16];
|
||||
u8 name[48];
|
||||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
Some resources entries are mere announcements, where the host is informed
|
||||
of specific remoteproc configuration. Other entries require the host to
|
||||
do something (e.g. reserve a requested resource) and possibly also reply
|
||||
by overwriting a member inside 'struct fw_resource' with info about the
|
||||
allocated resource.
|
||||
do something (e.g. allocate a system resource). Sometimes a negotiation
|
||||
is expected, where the firmware requests a resource, and once allocated,
|
||||
the host should provide back its details (e.g. address of an allocated
|
||||
memory region).
|
||||
|
||||
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
|
||||
|
@ -266,59 +278,45 @@ Here are the resource types that are currently being used:
|
|||
* memory region.
|
||||
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
|
||||
* @RSC_TRACE: announces the availability of a trace buffer into which
|
||||
* the remote processor will be writing logs. In this case,
|
||||
* 'da' indicates the device address where logs are written to,
|
||||
* and 'len' is the size of the trace buffer.
|
||||
* @RSC_VRING: request for allocation of a virtio vring (address should
|
||||
* be indicated in 'da', and 'len' should contain the number
|
||||
* of buffers supported by the vring).
|
||||
* @RSC_VIRTIO_DEV: announces support for a virtio device, and serves as
|
||||
* the virtio header. 'da' contains the virtio device
|
||||
* features, 'pa' holds the virtio guest features (host
|
||||
* will write them here after they're negotiated), 'len'
|
||||
* holds the virtio status, and 'flags' holds the virtio
|
||||
* device id (currently only VIRTIO_ID_RPMSG is supported).
|
||||
* the remote processor will be writing logs.
|
||||
* @RSC_VDEV: declare support for a virtio device, and serve as its
|
||||
* virtio header.
|
||||
* @RSC_LAST: just keep this one at the end
|
||||
*
|
||||
* Please note that these values are used as indices to the rproc_handle_rsc
|
||||
* lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
|
||||
* check the validity of an index before the lookup table is accessed, so
|
||||
* please update it as needed.
|
||||
*/
|
||||
enum fw_resource_type {
|
||||
RSC_CARVEOUT = 0,
|
||||
RSC_DEVMEM = 1,
|
||||
RSC_TRACE = 2,
|
||||
RSC_VRING = 3,
|
||||
RSC_VIRTIO_DEV = 4,
|
||||
RSC_VIRTIO_CFG = 5,
|
||||
RSC_VDEV = 3,
|
||||
RSC_LAST = 4,
|
||||
};
|
||||
|
||||
Most of the resource entries share the basic idea of address/length
|
||||
negotiation with the host: the firmware usually asks for memory
|
||||
of size 'len' bytes, and the host needs to allocate it and provide
|
||||
the device/physical address (when relevant) in 'da'/'pa' respectively.
|
||||
|
||||
If the firmware is compiled with hard coded device addresses, and
|
||||
can't handle dynamically allocated 'da' values, then the 'da' field
|
||||
will contain the expected device addresses (today we actually only support
|
||||
this scheme, as there aren't yet any use cases for dynamically allocated
|
||||
device addresses).
|
||||
For more details regarding a specific resource type, please see its
|
||||
dedicated structure in include/linux/remoteproc.h.
|
||||
|
||||
We also expect that platform-specific resource entries will show up
|
||||
at some point. When that happens, we could easily add a new RSC_PLAFORM
|
||||
at some point. When that happens, we could easily add a new RSC_PLATFORM
|
||||
type, and hand those resources to the platform-specific rproc driver to handle.
|
||||
|
||||
7. Virtio and remoteproc
|
||||
|
||||
The firmware should provide remoteproc information about virtio devices
|
||||
that it supports, and their configurations: a RSC_VIRTIO_DEV resource entry
|
||||
should specify the virtio device id, and subsequent RSC_VRING resource entries
|
||||
should indicate the vring size (i.e. how many buffers do they support) and
|
||||
where should they be mapped (i.e. which device address). Note: the alignment
|
||||
between the consumer and producer parts of the vring is assumed to be 4096.
|
||||
that it supports, and their configurations: a RSC_VDEV resource entry
|
||||
should specify the virtio device id (as in virtio_ids.h), virtio features,
|
||||
virtio config space, vrings information, etc.
|
||||
|
||||
At this point we only support a single virtio rpmsg device per remote
|
||||
processor, but the plan is to remove this limitation. In addition, once we
|
||||
move to TLV-based resource table, the plan is to have a single RSC_VIRTIO
|
||||
entry per supported virtio device, which will include the virtio header,
|
||||
the vrings information and the virtio config space.
|
||||
When a new remote processor is registered, the remoteproc framework
|
||||
will look for its resource table and will register the virtio devices
|
||||
it supports. A firmware may support any number of virtio devices, and
|
||||
of any type (a single remote processor can also easily support several
|
||||
rpmsg virtio devices this way, if desired).
|
||||
|
||||
Of course, RSC_VIRTIO resource entries are only good enough for static
|
||||
Of course, RSC_VDEV resource entries are only good enough for static
|
||||
allocation of virtio devices. Dynamic allocations will also be made possible
|
||||
using the rpmsg bus (similar to how we already do dynamic allocations of
|
||||
rpmsg channels; read more about it in rpmsg.txt).
|
||||
|
|
|
@ -8,11 +8,10 @@ config REMOTEPROC
|
|||
config OMAP_REMOTEPROC
|
||||
tristate "OMAP remoteproc support"
|
||||
depends on ARCH_OMAP4
|
||||
select OMAP_IOMMU
|
||||
depends on OMAP_IOMMU
|
||||
select REMOTEPROC
|
||||
select OMAP_MBOX_FWK
|
||||
select RPMSG
|
||||
default m
|
||||
help
|
||||
Say y here to support OMAP's remote processors (dual M3
|
||||
and DSP on OMAP4) via the remote processor framework.
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
obj-$(CONFIG_REMOTEPROC) += remoteproc.o
|
||||
remoteproc-y := remoteproc_core.o
|
||||
remoteproc-y += remoteproc_debugfs.o
|
||||
remoteproc-y += remoteproc_rpmsg.o
|
||||
remoteproc-y += remoteproc_virtio.o
|
||||
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
|
||||
|
|
|
@ -80,16 +80,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
|
|||
dev_info(dev, "received echo reply from %s\n", name);
|
||||
break;
|
||||
default:
|
||||
/* ignore vq indices which are too large to be valid */
|
||||
if (msg >= 2) {
|
||||
dev_warn(dev, "invalid mbox msg: 0x%x\n", msg);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, 'msg' contains the index of the vring
|
||||
* which was just triggered.
|
||||
*/
|
||||
/* msg contains the index of the triggered vring */
|
||||
if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
|
||||
dev_dbg(dev, "no message was found in vqid %d\n", msg);
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ static void klist_rproc_put(struct klist_node *n);
|
|||
* We need this in order to support name-based lookups (needed by the
|
||||
* rproc_get_by_name()).
|
||||
*
|
||||
* That said, we don't use rproc_get_by_name() anymore within the rpmsg
|
||||
* framework. The use cases that do require its existence should be
|
||||
* That said, we don't use rproc_get_by_name() at this point.
|
||||
* The use cases that do require its existence should be
|
||||
* scrutinized, and hopefully migrated to rproc_boot() using device-based
|
||||
* binding.
|
||||
*
|
||||
|
@ -63,9 +63,8 @@ static void klist_rproc_put(struct klist_node *n);
|
|||
static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put);
|
||||
|
||||
typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
|
||||
struct fw_resource *rsc, int len);
|
||||
typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
|
||||
struct fw_resource *rsc);
|
||||
struct resource_table *table, int len);
|
||||
typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
|
||||
|
||||
/*
|
||||
* This is the IOMMU fault handler we register with the IOMMU API
|
||||
|
@ -280,120 +279,39 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_handle_virtio_hdr() - handle a virtio header resource
|
||||
* @rproc: the remote processor
|
||||
* @rsc: the resource descriptor
|
||||
*
|
||||
* The existence of this virtio hdr resource entry means that the firmware
|
||||
* of this @rproc supports this virtio device.
|
||||
*
|
||||
* Currently we support only a single virtio device of type VIRTIO_ID_RPMSG,
|
||||
* but the plan is to remove this limitation and support any number
|
||||
* of virtio devices (and of any type). We'll also add support for dynamically
|
||||
* adding (and removing) virtio devices over the rpmsg bus, but small
|
||||
* firmwares that doesn't want to get involved with rpmsg will be able
|
||||
* to simple use the resource table for this.
|
||||
*
|
||||
* At this point this virtio header entry is rather simple: it just
|
||||
* announces the virtio device id and the supported virtio device features.
|
||||
* The plan though is to extend this to include the vring information and
|
||||
* the virtio config space, too (but first, some resource table overhaul
|
||||
* is needed: move from fixed-sized to variable-length TLV entries).
|
||||
*
|
||||
* For now, the 'flags' member of the resource entry contains the virtio
|
||||
* device id, the 'da' member contains the device features, and 'pa' is
|
||||
* where we need to store the guest features once negotiation completes.
|
||||
* As usual, the 'id' member of this resource contains the index of this
|
||||
* resource type (i.e. is this the first virtio hdr entry, the 2nd, ...).
|
||||
*
|
||||
* Returns 0 on success, or an appropriate error code otherwise
|
||||
*/
|
||||
static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc)
|
||||
{
|
||||
struct rproc_vdev *rvdev;
|
||||
|
||||
/* we only support VIRTIO_ID_RPMSG devices for now */
|
||||
if (rsc->flags != VIRTIO_ID_RPMSG) {
|
||||
dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* we only support a single vdev per rproc for now */
|
||||
if (rsc->id || rproc->rvdev) {
|
||||
dev_warn(rproc->dev, "redundant vdev entry: %s\n", rsc->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
|
||||
if (!rvdev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* remember the device features */
|
||||
rvdev->dfeatures = rsc->da;
|
||||
|
||||
rproc->rvdev = rvdev;
|
||||
rvdev->rproc = rproc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_handle_vring() - handle a vring fw resource
|
||||
* @rproc: the remote processor
|
||||
* @rsc: the vring resource descriptor
|
||||
*
|
||||
* This resource entry requires allocation of non-cacheable memory
|
||||
* for a virtio vring. Currently we only support two vrings per remote
|
||||
* processor, required for the virtio rpmsg device.
|
||||
*
|
||||
* The 'len' member of @rsc should contain the number of buffers this vring
|
||||
* support and 'da' should either contain the device address where
|
||||
* the remote processor is expecting the vring, or indicate that
|
||||
* dynamically allocation of the vring's device address is supported.
|
||||
*
|
||||
* Note: 'da' is currently not handled. This will be revised when the generic
|
||||
* iommu-based DMA API will arrive, or a dynanic & non-iommu use case show
|
||||
* up. Meanwhile, statically-addressed iommu-based images should use
|
||||
* RSC_DEVMEM resource entries to map their require 'da' to the physical
|
||||
* address of their base CMA region.
|
||||
*
|
||||
* 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_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
|
||||
{
|
||||
struct rproc *rproc = rvdev->rproc;
|
||||
struct device *dev = rproc->dev;
|
||||
struct rproc_vdev *rvdev = rproc->rvdev;
|
||||
struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
|
||||
dma_addr_t dma;
|
||||
int size, id = rsc->id;
|
||||
void *va;
|
||||
int ret, size, notifyid;
|
||||
|
||||
/* no vdev is in place ? */
|
||||
if (!rvdev) {
|
||||
dev_err(dev, "vring requested without a virtio dev entry\n");
|
||||
dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n",
|
||||
i, vring->da, vring->num, vring->align);
|
||||
|
||||
/* make sure reserved bytes are zeroes */
|
||||
if (vring->reserved) {
|
||||
dev_err(dev, "vring rsc has non zero reserved bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the firmware must provide the expected queue size */
|
||||
if (!rsc->len) {
|
||||
dev_err(dev, "missing expected queue size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* we currently support two vrings per rproc (for rx and tx) */
|
||||
if (id >= ARRAY_SIZE(rvdev->vring)) {
|
||||
dev_err(dev, "%s: invalid vring id %d\n", rsc->name, id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* have we already allocated this vring id ? */
|
||||
if (rvdev->vring[id].len) {
|
||||
dev_err(dev, "%s: duplicated id %d\n", rsc->name, id);
|
||||
/* verify queue size and vring alignment are sane */
|
||||
if (!vring->num || !vring->align) {
|
||||
dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
|
||||
vring->num, vring->align);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* actual size of vring (in bytes) */
|
||||
size = PAGE_ALIGN(vring_size(rsc->len, AMP_VRING_ALIGN));
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate non-cacheable memory for the vring. In the future
|
||||
|
@ -402,23 +320,141 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc)
|
|||
va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL);
|
||||
if (!va) {
|
||||
dev_err(dev, "dma_alloc_coherent failed\n");
|
||||
return -ENOMEM;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", id, va,
|
||||
dma, rsc->len, size);
|
||||
/* assign an rproc-wide unique index for this vring */
|
||||
/* TODO: assign a notifyid for rvdev updates as well */
|
||||
ret = idr_get_new(&rproc->notifyids, &rvdev->vring[i], ¬ifyid);
|
||||
if (ret) {
|
||||
dev_err(dev, "idr_get_new failed: %d\n", ret);
|
||||
dma_free_coherent(dev, size, va, dma);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rvdev->vring[id].len = rsc->len;
|
||||
rvdev->vring[id].va = va;
|
||||
rvdev->vring[id].dma = dma;
|
||||
/* let the rproc know the da and notifyid of this vring */
|
||||
/* TODO: expose this to remote processor */
|
||||
vring->da = dma;
|
||||
vring->notifyid = notifyid;
|
||||
|
||||
dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va,
|
||||
dma, size, notifyid);
|
||||
|
||||
rvdev->vring[i].len = vring->num;
|
||||
rvdev->vring[i].align = vring->align;
|
||||
rvdev->vring[i].va = va;
|
||||
rvdev->vring[i].dma = dma;
|
||||
rvdev->vring[i].notifyid = notifyid;
|
||||
rvdev->vring[i].rvdev = rvdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i)
|
||||
{
|
||||
struct rproc *rproc = rvdev->rproc;
|
||||
|
||||
for (i--; i > 0; i--) {
|
||||
struct rproc_vring *rvring = &rvdev->vring[i];
|
||||
int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
|
||||
|
||||
dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma);
|
||||
idr_remove(&rproc->notifyids, rvring->notifyid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_handle_vdev() - handle a vdev fw resource
|
||||
* @rproc: the remote processor
|
||||
* @rsc: the vring resource descriptor
|
||||
* @avail: size of available data (for sanity checking the image)
|
||||
*
|
||||
* This resource entry requests the host to statically register a virtio
|
||||
* device (vdev), and setup everything needed to support it. It contains
|
||||
* everything needed to make it possible: the virtio device id, virtio
|
||||
* device features, vrings information, virtio config space, etc...
|
||||
*
|
||||
* Before registering the vdev, the vrings are allocated from non-cacheable
|
||||
* physically contiguous memory. Currently we only support two vrings per
|
||||
* remote processor (temporary limitation). We might also want to consider
|
||||
* doing the vring allocation only later when ->find_vqs() is invoked, and
|
||||
* then release them upon ->del_vqs().
|
||||
*
|
||||
* Note: @da is currently not really handled correctly: we dynamically
|
||||
* allocate it using the DMA API, ignoring requested hard coded addresses,
|
||||
* and we don't take care of any required IOMMU programming. This is all
|
||||
* going to be taken care of when the generic iommu-based DMA API will be
|
||||
* merged. Meanwhile, statically-addressed iommu-based firmware images should
|
||||
* use RSC_DEVMEM resource entries to map their required @da to the physical
|
||||
* address of their base CMA region (ouch, hacky!).
|
||||
*
|
||||
* Returns 0 on success, or an appropriate error code otherwise
|
||||
*/
|
||||
static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
|
||||
int avail)
|
||||
{
|
||||
struct device *dev = rproc->dev;
|
||||
struct rproc_vdev *rvdev;
|
||||
int i, ret;
|
||||
|
||||
/* make sure resource isn't truncated */
|
||||
if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring)
|
||||
+ rsc->config_len > avail) {
|
||||
dev_err(rproc->dev, "vdev rsc is truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure reserved bytes are zeroes */
|
||||
if (rsc->reserved[0] || rsc->reserved[1]) {
|
||||
dev_err(dev, "vdev rsc has non zero reserved bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n",
|
||||
rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);
|
||||
|
||||
/* we currently support only two vrings per rvdev */
|
||||
if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) {
|
||||
dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
|
||||
if (!rvdev)
|
||||
return -ENOMEM;
|
||||
|
||||
rvdev->rproc = rproc;
|
||||
|
||||
/* allocate the vrings */
|
||||
for (i = 0; i < rsc->num_of_vrings; i++) {
|
||||
ret = __rproc_handle_vring(rvdev, rsc, i);
|
||||
if (ret)
|
||||
goto free_vrings;
|
||||
}
|
||||
|
||||
/* remember the device features */
|
||||
rvdev->dfeatures = rsc->dfeatures;
|
||||
|
||||
list_add_tail(&rvdev->node, &rproc->rvdevs);
|
||||
|
||||
/* it is now safe to add the virtio device */
|
||||
ret = rproc_add_virtio_dev(rvdev, rsc->id);
|
||||
if (ret)
|
||||
goto free_vrings;
|
||||
|
||||
return 0;
|
||||
|
||||
free_vrings:
|
||||
__rproc_free_vrings(rvdev, i);
|
||||
kfree(rvdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_handle_trace() - handle a shared trace buffer resource
|
||||
* @rproc: the remote processor
|
||||
* @rsc: the trace resource descriptor
|
||||
* @avail: size of available data (for sanity checking the image)
|
||||
*
|
||||
* In case the remote processor dumps trace logs into memory,
|
||||
* export it via debugfs.
|
||||
|
@ -430,13 +466,25 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc)
|
|||
*
|
||||
* Returns 0 on success, or an appropriate error code otherwise
|
||||
*/
|
||||
static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
|
||||
static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
|
||||
int avail)
|
||||
{
|
||||
struct rproc_mem_entry *trace;
|
||||
struct device *dev = rproc->dev;
|
||||
void *ptr;
|
||||
char name[15];
|
||||
|
||||
if (sizeof(*rsc) > avail) {
|
||||
dev_err(rproc->dev, "trace rsc is truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure reserved bytes are zeroes */
|
||||
if (rsc->reserved) {
|
||||
dev_err(dev, "trace rsc has non zero reserved bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* what's the kernel address of this resource ? */
|
||||
ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
|
||||
if (!ptr) {
|
||||
|
@ -469,7 +517,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
|
|||
|
||||
rproc->num_traces++;
|
||||
|
||||
dev_dbg(dev, "%s added: va %p, da 0x%llx, len 0x%x\n", name, ptr,
|
||||
dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr,
|
||||
rsc->da, rsc->len);
|
||||
|
||||
return 0;
|
||||
|
@ -479,6 +527,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
|
|||
* rproc_handle_devmem() - handle devmem resource entry
|
||||
* @rproc: remote processor handle
|
||||
* @rsc: the devmem resource entry
|
||||
* @avail: size of available data (for sanity checking the image)
|
||||
*
|
||||
* Remote processors commonly need to access certain on-chip peripherals.
|
||||
*
|
||||
|
@ -499,7 +548,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
|
|||
* and not allow firmwares to request access to physical addresses that
|
||||
* are outside those ranges.
|
||||
*/
|
||||
static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
|
||||
static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
|
||||
int avail)
|
||||
{
|
||||
struct rproc_mem_entry *mapping;
|
||||
int ret;
|
||||
|
@ -508,6 +558,17 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
|
|||
if (!rproc->domain)
|
||||
return -EINVAL;
|
||||
|
||||
if (sizeof(*rsc) > avail) {
|
||||
dev_err(rproc->dev, "devmem rsc is truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure reserved bytes are zeroes */
|
||||
if (rsc->reserved) {
|
||||
dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
|
||||
if (!mapping) {
|
||||
dev_err(rproc->dev, "kzalloc mapping failed\n");
|
||||
|
@ -531,7 +592,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
|
|||
mapping->len = rsc->len;
|
||||
list_add_tail(&mapping->node, &rproc->mappings);
|
||||
|
||||
dev_dbg(rproc->dev, "mapped devmem pa 0x%llx, da 0x%llx, len 0x%x\n",
|
||||
dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n",
|
||||
rsc->pa, rsc->da, rsc->len);
|
||||
|
||||
return 0;
|
||||
|
@ -545,6 +606,7 @@ out:
|
|||
* rproc_handle_carveout() - handle phys contig memory allocation requests
|
||||
* @rproc: rproc handle
|
||||
* @rsc: the resource entry
|
||||
* @avail: size of available data (for image validation)
|
||||
*
|
||||
* This function will handle firmware requests for allocation of physically
|
||||
* contiguous memory regions.
|
||||
|
@ -558,7 +620,8 @@ out:
|
|||
* needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
|
||||
* pressure is important; it may have a substantial impact on performance.
|
||||
*/
|
||||
static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
|
||||
static int rproc_handle_carveout(struct rproc *rproc,
|
||||
struct fw_rsc_carveout *rsc, int avail)
|
||||
{
|
||||
struct rproc_mem_entry *carveout, *mapping;
|
||||
struct device *dev = rproc->dev;
|
||||
|
@ -566,6 +629,20 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
|
|||
void *va;
|
||||
int ret;
|
||||
|
||||
if (sizeof(*rsc) > avail) {
|
||||
dev_err(rproc->dev, "carveout rsc is truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure reserved bytes are zeroes */
|
||||
if (rsc->reserved) {
|
||||
dev_err(dev, "carveout rsc has non zero reserved bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n",
|
||||
rsc->da, rsc->pa, rsc->len, rsc->flags);
|
||||
|
||||
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
|
||||
if (!mapping) {
|
||||
dev_err(dev, "kzalloc mapping failed\n");
|
||||
|
@ -624,7 +701,7 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
|
|||
mapping->len = rsc->len;
|
||||
list_add_tail(&mapping->node, &rproc->mappings);
|
||||
|
||||
dev_dbg(dev, "carveout mapped 0x%llx to 0x%x\n", rsc->da, dma);
|
||||
dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma);
|
||||
|
||||
/*
|
||||
* Some remote processors might need to know the pa
|
||||
|
@ -665,36 +742,44 @@ free_mapping:
|
|||
* enum fw_resource_type.
|
||||
*/
|
||||
static rproc_handle_resource_t rproc_handle_rsc[] = {
|
||||
[RSC_CARVEOUT] = rproc_handle_carveout,
|
||||
[RSC_DEVMEM] = rproc_handle_devmem,
|
||||
[RSC_TRACE] = rproc_handle_trace,
|
||||
[RSC_VRING] = rproc_handle_vring,
|
||||
[RSC_VIRTIO_DEV] = NULL, /* handled early upon registration */
|
||||
[RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
|
||||
[RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
|
||||
[RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
|
||||
[RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
|
||||
};
|
||||
|
||||
/* handle firmware resource entries before booting the remote processor */
|
||||
static int
|
||||
rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
|
||||
rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
|
||||
{
|
||||
struct device *dev = rproc->dev;
|
||||
rproc_handle_resource_t handler;
|
||||
int ret = 0;
|
||||
int ret = 0, i;
|
||||
|
||||
for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) {
|
||||
dev_dbg(dev, "rsc: type %d, da 0x%llx, pa 0x%llx, len 0x%x, "
|
||||
"id %d, name %s, flags %x\n", rsc->type, rsc->da,
|
||||
rsc->pa, rsc->len, rsc->id, rsc->name, rsc->flags);
|
||||
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);
|
||||
|
||||
if (rsc->type >= RSC_LAST) {
|
||||
dev_warn(dev, "unsupported resource %d\n", rsc->type);
|
||||
/* make sure table isn't truncated */
|
||||
if (avail < 0) {
|
||||
dev_err(dev, "rsc table is truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "rsc: type %d\n", hdr->type);
|
||||
|
||||
if (hdr->type >= RSC_LAST) {
|
||||
dev_warn(dev, "unsupported resource %d\n", hdr->type);
|
||||
continue;
|
||||
}
|
||||
|
||||
handler = rproc_handle_rsc[rsc->type];
|
||||
handler = rproc_handle_rsc[hdr->type];
|
||||
if (!handler)
|
||||
continue;
|
||||
|
||||
ret = handler(rproc, rsc);
|
||||
ret = handler(rproc, rsc, avail);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
@ -704,47 +789,64 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
|
|||
|
||||
/* handle firmware resource entries while registering the remote processor */
|
||||
static int
|
||||
rproc_handle_virtio_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
|
||||
rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
|
||||
{
|
||||
struct device *dev = rproc->dev;
|
||||
int ret = -ENODEV;
|
||||
int ret = 0, i;
|
||||
|
||||
for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc))
|
||||
if (rsc->type == RSC_VIRTIO_DEV) {
|
||||
dev_dbg(dev, "found vdev %d/%s features %llx\n",
|
||||
rsc->flags, rsc->name, rsc->da);
|
||||
ret = rproc_handle_virtio_hdr(rproc, rsc);
|
||||
break;
|
||||
for (i = 0; i < table->num; i++) {
|
||||
int offset = table->offset[i];
|
||||
struct fw_rsc_hdr *hdr = (void *)table + offset;
|
||||
int avail = len - offset - sizeof(*hdr);
|
||||
struct fw_rsc_vdev *vrsc;
|
||||
|
||||
/* make sure table isn't truncated */
|
||||
if (avail < 0) {
|
||||
dev_err(dev, "rsc table is truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
|
||||
|
||||
if (hdr->type != RSC_VDEV)
|
||||
continue;
|
||||
|
||||
vrsc = (struct fw_rsc_vdev *)hdr->data;
|
||||
|
||||
ret = rproc_handle_vdev(rproc, vrsc, avail);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_handle_resources() - find and handle the resource table
|
||||
* rproc_find_rsc_table() - find the resource table
|
||||
* @rproc: the rproc handle
|
||||
* @elf_data: the content of the ELF firmware image
|
||||
* @len: firmware size (in bytes)
|
||||
* @handler: function that should be used to handle the resource table
|
||||
* @tablesz: place holder for providing back the table size
|
||||
*
|
||||
* This function finds the resource table inside the remote processor's
|
||||
* firmware, and invoke a user-supplied handler with it (we have two
|
||||
* possible handlers: one is invoked upon registration of @rproc,
|
||||
* in order to register the supported virito devices, and the other is
|
||||
* invoked when @rproc is actually booted).
|
||||
* firmware. It is used both upon the registration of @rproc (in order
|
||||
* to look for and register the supported virito devices), and when the
|
||||
* @rproc is booted.
|
||||
*
|
||||
* Currently this function fails if a resource table doesn't exist.
|
||||
* This restriction will be removed when we'll start supporting remote
|
||||
* processors that don't need a resource table.
|
||||
* Returns the pointer to the resource table if it is found, and write its
|
||||
* size into @tablesz. If a valid table isn't found, NULL is returned
|
||||
* (and @tablesz isn't set).
|
||||
*/
|
||||
static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
|
||||
size_t len, rproc_handle_resources_t handler)
|
||||
|
||||
static struct resource_table *
|
||||
rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len,
|
||||
int *tablesz)
|
||||
{
|
||||
struct elf32_hdr *ehdr;
|
||||
struct elf32_shdr *shdr;
|
||||
const char *name_table;
|
||||
int i, ret = -EINVAL;
|
||||
struct device *dev = rproc->dev;
|
||||
struct resource_table *table = NULL;
|
||||
int i;
|
||||
|
||||
ehdr = (struct elf32_hdr *)elf_data;
|
||||
shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
|
||||
|
@ -752,24 +854,50 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
|
|||
|
||||
/* look for the resource table and handle it */
|
||||
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
|
||||
if (!strcmp(name_table + shdr->sh_name, ".resource_table")) {
|
||||
struct fw_resource *table = (struct fw_resource *)
|
||||
(elf_data + shdr->sh_offset);
|
||||
int size = shdr->sh_size;
|
||||
int offset = shdr->sh_offset;
|
||||
|
||||
if (shdr->sh_offset + shdr->sh_size > len) {
|
||||
dev_err(rproc->dev,
|
||||
"truncated fw: need 0x%x avail 0x%x\n",
|
||||
shdr->sh_offset + shdr->sh_size, len);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (strcmp(name_table + shdr->sh_name, ".resource_table"))
|
||||
continue;
|
||||
|
||||
ret = handler(rproc, table, shdr->sh_size);
|
||||
table = (struct resource_table *)(elf_data + offset);
|
||||
|
||||
break;
|
||||
/* make sure we have the entire table */
|
||||
if (offset + size > len) {
|
||||
dev_err(dev, "resource table truncated\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make sure table has at least the header */
|
||||
if (sizeof(struct resource_table) > size) {
|
||||
dev_err(dev, "header-less resource table\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we don't support any version beyond the first */
|
||||
if (table->ver != 1) {
|
||||
dev_err(dev, "unsupported fw ver: %d\n", table->ver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make sure reserved bytes are zeroes */
|
||||
if (table->reserved[0] || table->reserved[1]) {
|
||||
dev_err(dev, "non zero reserved bytes\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make sure the offsets array isn't truncated */
|
||||
if (table->num * sizeof(table->offset[0]) +
|
||||
sizeof(struct resource_table) > size) {
|
||||
dev_err(dev, "resource table incomplete\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*tablesz = shdr->sh_size;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -777,14 +905,12 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
|
|||
* @rproc: rproc handle
|
||||
*
|
||||
* This function will free all resources acquired for @rproc, and it
|
||||
* is called when @rproc shuts down, or just failed booting.
|
||||
* is called whenever @rproc either shuts down or fails to boot.
|
||||
*/
|
||||
static void rproc_resource_cleanup(struct rproc *rproc)
|
||||
{
|
||||
struct rproc_mem_entry *entry, *tmp;
|
||||
struct device *dev = rproc->dev;
|
||||
struct rproc_vdev *rvdev = rproc->rvdev;
|
||||
int i;
|
||||
|
||||
/* clean up debugfs trace entries */
|
||||
list_for_each_entry_safe(entry, tmp, &rproc->traces, node) {
|
||||
|
@ -794,23 +920,6 @@ static void rproc_resource_cleanup(struct rproc *rproc)
|
|||
kfree(entry);
|
||||
}
|
||||
|
||||
/* free the coherent memory allocated for the vrings */
|
||||
for (i = 0; rvdev && i < ARRAY_SIZE(rvdev->vring); i++) {
|
||||
int qsz = rvdev->vring[i].len;
|
||||
void *va = rvdev->vring[i].va;
|
||||
int dma = rvdev->vring[i].dma;
|
||||
|
||||
/* virtqueue size is expressed in number of buffers supported */
|
||||
if (qsz) {
|
||||
/* how many bytes does this vring really occupy ? */
|
||||
int size = PAGE_ALIGN(vring_size(qsz, AMP_VRING_ALIGN));
|
||||
|
||||
dma_free_coherent(rproc->dev, size, va, dma);
|
||||
|
||||
rvdev->vring[i].len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* clean up carveout allocations */
|
||||
list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
|
||||
dma_free_coherent(dev, entry->len, entry->va, entry->dma);
|
||||
|
@ -840,6 +949,7 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
|||
const char *name = rproc->firmware;
|
||||
struct device *dev = rproc->dev;
|
||||
struct elf32_hdr *ehdr;
|
||||
char class;
|
||||
|
||||
if (!fw) {
|
||||
dev_err(dev, "failed to load %s\n", name);
|
||||
|
@ -853,6 +963,13 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
|||
|
||||
ehdr = (struct elf32_hdr *)fw->data;
|
||||
|
||||
/* We only support ELF32 at this point */
|
||||
class = ehdr->e_ident[EI_CLASS];
|
||||
if (class != ELFCLASS32) {
|
||||
dev_err(dev, "Unsupported class: %d\n", class);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We assume the firmware has the same endianess as the host */
|
||||
# ifdef __LITTLE_ENDIAN
|
||||
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
|
@ -894,7 +1011,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
|
|||
struct device *dev = rproc->dev;
|
||||
const char *name = rproc->firmware;
|
||||
struct elf32_hdr *ehdr;
|
||||
int ret;
|
||||
struct resource_table *table;
|
||||
int ret, tablesz;
|
||||
|
||||
ret = rproc_fw_sanity_check(rproc, fw);
|
||||
if (ret)
|
||||
|
@ -921,9 +1039,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
|
|||
*/
|
||||
rproc->bootaddr = ehdr->e_entry;
|
||||
|
||||
/* look for the resource table */
|
||||
table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz);
|
||||
if (!table)
|
||||
goto clean_up;
|
||||
|
||||
/* handle fw resources which are required to boot rproc */
|
||||
ret = rproc_handle_resources(rproc, fw->data, fw->size,
|
||||
rproc_handle_boot_rsc);
|
||||
ret = rproc_handle_boot_rsc(rproc, table, tablesz);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to process resources: %d\n", ret);
|
||||
goto clean_up;
|
||||
|
@ -966,22 +1088,19 @@ clean_up:
|
|||
static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
|
||||
{
|
||||
struct rproc *rproc = context;
|
||||
struct device *dev = rproc->dev;
|
||||
int ret;
|
||||
struct resource_table *table;
|
||||
int ret, tablesz;
|
||||
|
||||
if (rproc_fw_sanity_check(rproc, fw) < 0)
|
||||
goto out;
|
||||
|
||||
/* does the fw supports any virtio devices ? */
|
||||
ret = rproc_handle_resources(rproc, fw->data, fw->size,
|
||||
rproc_handle_virtio_rsc);
|
||||
if (ret) {
|
||||
dev_info(dev, "No fw virtio device was found\n");
|
||||
/* look for the resource table */
|
||||
table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz);
|
||||
if (!table)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* add the virtio device (currently only rpmsg vdevs are supported) */
|
||||
ret = rproc_add_rpmsg_vdev(rproc);
|
||||
/* look for virtio devices and register them */
|
||||
ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -1146,13 +1265,23 @@ EXPORT_SYMBOL(rproc_shutdown);
|
|||
void rproc_release(struct kref *kref)
|
||||
{
|
||||
struct rproc *rproc = container_of(kref, struct rproc, refcount);
|
||||
struct rproc_vdev *rvdev, *rvtmp;
|
||||
|
||||
dev_info(rproc->dev, "removing %s\n", rproc->name);
|
||||
|
||||
rproc_delete_debug_dir(rproc);
|
||||
|
||||
/* at this point no one holds a reference to rproc anymore */
|
||||
kfree(rproc);
|
||||
/* clean up remote vdev entries */
|
||||
list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) {
|
||||
__rproc_free_vrings(rvdev, RVDEV_NUM_VRINGS);
|
||||
list_del(&rvdev->node);
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point no one holds a reference to rproc anymore,
|
||||
* so we can directly unroll rproc_alloc()
|
||||
*/
|
||||
rproc_free(rproc);
|
||||
}
|
||||
|
||||
/* will be called when an rproc is added to the rprocs klist */
|
||||
|
@ -1196,7 +1325,7 @@ static struct rproc *next_rproc(struct klist_iter *i)
|
|||
* use rproc_put() to decrement it back once rproc isn't needed anymore.
|
||||
*
|
||||
* Note: currently this function (and its counterpart rproc_put()) are not
|
||||
* used anymore by the rpmsg subsystem. We need to scrutinize the use cases
|
||||
* being used. We need to scrutinize the use cases
|
||||
* that still need them, and see if we can migrate them to use the non
|
||||
* name-based boot/shutdown interface.
|
||||
*/
|
||||
|
@ -1271,11 +1400,8 @@ EXPORT_SYMBOL(rproc_put);
|
|||
* firmware.
|
||||
*
|
||||
* If found, those virtio devices will be created and added, so as a result
|
||||
* of registering this remote processor, additional virtio drivers will be
|
||||
* of registering this remote processor, additional virtio drivers might be
|
||||
* probed.
|
||||
*
|
||||
* Currently, though, we only support a single RPMSG virtio vdev per remote
|
||||
* processor.
|
||||
*/
|
||||
int rproc_register(struct rproc *rproc)
|
||||
{
|
||||
|
@ -1298,7 +1424,7 @@ int rproc_register(struct rproc *rproc)
|
|||
|
||||
/*
|
||||
* We must retrieve early virtio configuration info from
|
||||
* the firmware (e.g. whether to register a virtio rpmsg device,
|
||||
* the firmware (e.g. whether to register a virtio device,
|
||||
* what virtio features does it support, ...).
|
||||
*
|
||||
* We're initiating an asynchronous firmware loading, so we can
|
||||
|
@ -1367,9 +1493,12 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
|
|||
|
||||
mutex_init(&rproc->lock);
|
||||
|
||||
idr_init(&rproc->notifyids);
|
||||
|
||||
INIT_LIST_HEAD(&rproc->carveouts);
|
||||
INIT_LIST_HEAD(&rproc->mappings);
|
||||
INIT_LIST_HEAD(&rproc->traces);
|
||||
INIT_LIST_HEAD(&rproc->rvdevs);
|
||||
|
||||
rproc->state = RPROC_OFFLINE;
|
||||
|
||||
|
@ -1389,6 +1518,9 @@ EXPORT_SYMBOL(rproc_alloc);
|
|||
*/
|
||||
void rproc_free(struct rproc *rproc)
|
||||
{
|
||||
idr_remove_all(&rproc->notifyids);
|
||||
idr_destroy(&rproc->notifyids);
|
||||
|
||||
kfree(rproc);
|
||||
}
|
||||
EXPORT_SYMBOL(rproc_free);
|
||||
|
@ -1415,18 +1547,22 @@ EXPORT_SYMBOL(rproc_free);
|
|||
*/
|
||||
int rproc_unregister(struct rproc *rproc)
|
||||
{
|
||||
struct rproc_vdev *rvdev;
|
||||
|
||||
if (!rproc)
|
||||
return -EINVAL;
|
||||
|
||||
/* if rproc is just being registered, wait */
|
||||
wait_for_completion(&rproc->firmware_loading_complete);
|
||||
|
||||
/* was an rpmsg vdev created ? */
|
||||
if (rproc->rvdev)
|
||||
rproc_remove_rpmsg_vdev(rproc);
|
||||
/* clean up remote vdev entries */
|
||||
list_for_each_entry(rvdev, &rproc->rvdevs, node)
|
||||
rproc_remove_virtio_dev(rvdev);
|
||||
|
||||
klist_remove(&rproc->node);
|
||||
/* the rproc is downref'ed as soon as it's removed from the klist */
|
||||
klist_del(&rproc->node);
|
||||
|
||||
/* the rproc will only be released after its refcount drops to zero */
|
||||
kref_put(&rproc->refcount, rproc_release);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -28,9 +28,9 @@ struct rproc;
|
|||
void rproc_release(struct kref *kref);
|
||||
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
|
||||
|
||||
/* from remoteproc_rpmsg.c */
|
||||
int rproc_add_rpmsg_vdev(struct rproc *);
|
||||
void rproc_remove_rpmsg_vdev(struct rproc *rproc);
|
||||
/* from remoteproc_virtio.c */
|
||||
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
|
||||
void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
|
||||
|
||||
/* from remoteproc_debugfs.c */
|
||||
void rproc_remove_trace_file(struct dentry *tfile);
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#include <linux/export.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
|
@ -30,45 +29,41 @@
|
|||
|
||||
#include "remoteproc_internal.h"
|
||||
|
||||
/**
|
||||
* struct rproc_virtio_vq_info - virtqueue state
|
||||
* @vq_id: a unique index of this virtqueue (unique for this @rproc)
|
||||
* @rproc: handle to the remote processor
|
||||
*
|
||||
* Such a struct will be maintained for every virtqueue we're
|
||||
* using to communicate with the remote processor
|
||||
*/
|
||||
struct rproc_virtio_vq_info {
|
||||
__u16 vq_id;
|
||||
struct rproc *rproc;
|
||||
};
|
||||
|
||||
/* kick the remote processor, and let it know which virtqueue to poke at */
|
||||
static void rproc_virtio_notify(struct virtqueue *vq)
|
||||
{
|
||||
struct rproc_virtio_vq_info *rpvq = vq->priv;
|
||||
struct rproc *rproc = rpvq->rproc;
|
||||
struct rproc_vring *rvring = vq->priv;
|
||||
struct rproc *rproc = rvring->rvdev->rproc;
|
||||
int notifyid = rvring->notifyid;
|
||||
|
||||
dev_dbg(rproc->dev, "kicking vq id: %d\n", rpvq->vq_id);
|
||||
dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid);
|
||||
|
||||
rproc->ops->kick(rproc, rpvq->vq_id);
|
||||
rproc->ops->kick(rproc, notifyid);
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
|
||||
* @rproc: handle to the remote processor
|
||||
* @vq_id: index of the signalled virtqueue
|
||||
* @notifyid: index of the signalled virtqueue (unique per this @rproc)
|
||||
*
|
||||
* This function should be called by the platform-specific rproc driver,
|
||||
* when the remote processor signals that a specific virtqueue has pending
|
||||
* messages available.
|
||||
*
|
||||
* Returns IRQ_NONE if no message was found in the @vq_id virtqueue,
|
||||
* Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
|
||||
* and otherwise returns IRQ_HANDLED.
|
||||
*/
|
||||
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id)
|
||||
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
|
||||
{
|
||||
return vring_interrupt(0, rproc->rvdev->vq[vq_id]);
|
||||
struct rproc_vring *rvring;
|
||||
|
||||
dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid);
|
||||
|
||||
rvring = idr_find(&rproc->notifyids, notifyid);
|
||||
if (!rvring || !rvring->vq)
|
||||
return IRQ_NONE;
|
||||
|
||||
return vring_interrupt(0, rvring->vq);
|
||||
}
|
||||
EXPORT_SYMBOL(rproc_vq_interrupt);
|
||||
|
||||
|
@ -77,60 +72,60 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
|
|||
void (*callback)(struct virtqueue *vq),
|
||||
const char *name)
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct rproc *rproc = vdev_to_rproc(vdev);
|
||||
struct rproc_vdev *rvdev = rproc->rvdev;
|
||||
struct rproc_virtio_vq_info *rpvq;
|
||||
struct rproc_vring *rvring;
|
||||
struct virtqueue *vq;
|
||||
void *addr;
|
||||
int ret, len;
|
||||
int len, size;
|
||||
|
||||
rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL);
|
||||
if (!rpvq)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
/* we're temporarily limited to two virtqueues per rvdev */
|
||||
if (id >= ARRAY_SIZE(rvdev->vring))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
rpvq->rproc = rproc;
|
||||
rpvq->vq_id = id;
|
||||
rvring = &rvdev->vring[id];
|
||||
|
||||
addr = rvdev->vring[id].va;
|
||||
len = rvdev->vring[id].len;
|
||||
addr = rvring->va;
|
||||
len = rvring->len;
|
||||
|
||||
dev_dbg(rproc->dev, "vring%d: va %p qsz %d\n", id, addr, len);
|
||||
/* zero vring */
|
||||
size = vring_size(len, rvring->align);
|
||||
memset(addr, 0, size);
|
||||
|
||||
dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n",
|
||||
id, addr, len, rvring->notifyid);
|
||||
|
||||
/*
|
||||
* Create the new vq, and tell virtio we're not interested in
|
||||
* the 'weak' smp barriers, since we're talking with a real device.
|
||||
*/
|
||||
vq = vring_new_virtqueue(len, AMP_VRING_ALIGN, vdev, false, addr,
|
||||
vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr,
|
||||
rproc_virtio_notify, callback, name);
|
||||
if (!vq) {
|
||||
dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name);
|
||||
ret = -ENOMEM;
|
||||
goto free_rpvq;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
rvdev->vq[id] = vq;
|
||||
vq->priv = rpvq;
|
||||
rvring->vq = vq;
|
||||
vq->priv = rvring;
|
||||
|
||||
return vq;
|
||||
|
||||
free_rpvq:
|
||||
kfree(rpvq);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void rproc_virtio_del_vqs(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtqueue *vq, *n;
|
||||
struct rproc *rproc = vdev_to_rproc(vdev);
|
||||
struct rproc_vring *rvring;
|
||||
|
||||
/* power down the remote processor before deleting vqs */
|
||||
rproc_shutdown(rproc);
|
||||
|
||||
list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
|
||||
struct rproc_virtio_vq_info *rpvq = vq->priv;
|
||||
rvring = vq->priv;
|
||||
rvring->vq = NULL;
|
||||
vring_del_virtqueue(vq);
|
||||
kfree(rpvq);
|
||||
}
|
||||
|
||||
/* power down the remote processor */
|
||||
rproc_shutdown(rproc);
|
||||
}
|
||||
|
||||
static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
|
||||
|
@ -141,17 +136,6 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
|
|||
struct rproc *rproc = vdev_to_rproc(vdev);
|
||||
int i, ret;
|
||||
|
||||
/* we maintain two virtqueues per remote processor (for RX and TX) */
|
||||
if (nvqs != 2)
|
||||
return -EINVAL;
|
||||
|
||||
/* boot the remote processor */
|
||||
ret = rproc_boot(rproc);
|
||||
if (ret) {
|
||||
dev_err(rproc->dev, "rproc_boot() failed %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < nvqs; ++i) {
|
||||
vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
|
||||
if (IS_ERR(vqs[i])) {
|
||||
|
@ -160,6 +144,13 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
|
|||
}
|
||||
}
|
||||
|
||||
/* now that the vqs are all set, boot the remote processor */
|
||||
ret = rproc_boot(rproc);
|
||||
if (ret) {
|
||||
dev_err(rproc->dev, "rproc_boot() failed %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
@ -170,7 +161,7 @@ error:
|
|||
/*
|
||||
* We don't support yet real virtio status semantics.
|
||||
*
|
||||
* The plan is to provide this via the VIRTIO HDR resource entry
|
||||
* The plan is to provide this via the VDEV resource entry
|
||||
* which is part of the firmware: this way the remote processor
|
||||
* will be able to access the status values as set by us.
|
||||
*/
|
||||
|
@ -181,7 +172,7 @@ static u8 rproc_virtio_get_status(struct virtio_device *vdev)
|
|||
|
||||
static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
|
||||
{
|
||||
dev_dbg(&vdev->dev, "new status: %d\n", status);
|
||||
dev_dbg(&vdev->dev, "status: %d\n", status);
|
||||
}
|
||||
|
||||
static void rproc_virtio_reset(struct virtio_device *vdev)
|
||||
|
@ -192,15 +183,14 @@ static void rproc_virtio_reset(struct virtio_device *vdev)
|
|||
/* provide the vdev features as retrieved from the firmware */
|
||||
static u32 rproc_virtio_get_features(struct virtio_device *vdev)
|
||||
{
|
||||
struct rproc *rproc = vdev_to_rproc(vdev);
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
|
||||
/* we only support a single vdev device for now */
|
||||
return rproc->rvdev->dfeatures;
|
||||
return rvdev->dfeatures;
|
||||
}
|
||||
|
||||
static void rproc_virtio_finalize_features(struct virtio_device *vdev)
|
||||
{
|
||||
struct rproc *rproc = vdev_to_rproc(vdev);
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
|
||||
/* Give virtio_ring a chance to accept features */
|
||||
vring_transport_features(vdev);
|
||||
|
@ -214,7 +204,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev)
|
|||
* fixed as part of a small resource table overhaul and then an
|
||||
* extension of the virtio resource entries.
|
||||
*/
|
||||
rproc->rvdev->gfeatures = vdev->features[0];
|
||||
rvdev->gfeatures = vdev->features[0];
|
||||
}
|
||||
|
||||
static struct virtio_config_ops rproc_virtio_config_ops = {
|
||||
|
@ -244,26 +234,25 @@ static void rproc_vdev_release(struct device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* rproc_add_rpmsg_vdev() - create an rpmsg virtio device
|
||||
* @rproc: the rproc handle
|
||||
* rproc_add_virtio_dev() - register an rproc-induced virtio device
|
||||
* @rvdev: the remote vdev
|
||||
*
|
||||
* This function is called if virtio rpmsg support was found in the
|
||||
* firmware of the remote processor.
|
||||
* This function registers a virtio device. This vdev's partent is
|
||||
* the rproc device.
|
||||
*
|
||||
* Today we only support creating a single rpmsg vdev (virtio device),
|
||||
* but the plan is to remove this limitation. At that point this interface
|
||||
* will be revised/extended.
|
||||
* Returns 0 on success or an appropriate error value otherwise.
|
||||
*/
|
||||
int rproc_add_rpmsg_vdev(struct rproc *rproc)
|
||||
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
|
||||
{
|
||||
struct rproc *rproc = rvdev->rproc;
|
||||
struct device *dev = rproc->dev;
|
||||
struct rproc_vdev *rvdev = rproc->rvdev;
|
||||
struct virtio_device *vdev = &rvdev->vdev;
|
||||
int ret;
|
||||
|
||||
rvdev->vdev.id.device = VIRTIO_ID_RPMSG,
|
||||
rvdev->vdev.config = &rproc_virtio_config_ops,
|
||||
rvdev->vdev.dev.parent = dev;
|
||||
rvdev->vdev.dev.release = rproc_vdev_release;
|
||||
vdev->id.device = id,
|
||||
vdev->config = &rproc_virtio_config_ops,
|
||||
vdev->dev.parent = dev;
|
||||
vdev->dev.release = rproc_vdev_release;
|
||||
|
||||
/*
|
||||
* We're indirectly making a non-temporary copy of the rproc pointer
|
||||
|
@ -275,25 +264,26 @@ int rproc_add_rpmsg_vdev(struct rproc *rproc)
|
|||
*/
|
||||
kref_get(&rproc->refcount);
|
||||
|
||||
ret = register_virtio_device(&rvdev->vdev);
|
||||
ret = register_virtio_device(vdev);
|
||||
if (ret) {
|
||||
kref_put(&rproc->refcount, rproc_release);
|
||||
dev_err(dev, "failed to register vdev: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device
|
||||
* @rproc: the rproc handle
|
||||
* rproc_remove_virtio_dev() - remove an rproc-induced virtio device
|
||||
* @rvdev: the remote vdev
|
||||
*
|
||||
* This function is called whenever @rproc is removed _iff_ an rpmsg
|
||||
* vdev was created beforehand.
|
||||
* This function unregisters an existing virtio device.
|
||||
*/
|
||||
void rproc_remove_rpmsg_vdev(struct rproc *rproc)
|
||||
void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
|
||||
{
|
||||
struct rproc_vdev *rvdev = rproc->rvdev;
|
||||
|
||||
unregister_virtio_device(&rvdev->vdev);
|
||||
}
|
|
@ -289,6 +289,26 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
|
|||
}
|
||||
EXPORT_SYMBOL(rpmsg_create_ept);
|
||||
|
||||
/**
|
||||
* __rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
|
||||
* @vrp: virtproc which owns this ept
|
||||
* @ept: endpoing to destroy
|
||||
*
|
||||
* An internal function which destroy an ept without assuming it is
|
||||
* bound to an rpmsg channel. This is needed for handling the internal
|
||||
* name service endpoint, which isn't bound to an rpmsg channel.
|
||||
* See also __rpmsg_create_ept().
|
||||
*/
|
||||
static void
|
||||
__rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept)
|
||||
{
|
||||
mutex_lock(&vrp->endpoints_lock);
|
||||
idr_remove(&vrp->endpoints, ept->addr);
|
||||
mutex_unlock(&vrp->endpoints_lock);
|
||||
|
||||
kfree(ept);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
|
||||
* @ept: endpoing to destroy
|
||||
|
@ -298,13 +318,7 @@ EXPORT_SYMBOL(rpmsg_create_ept);
|
|||
*/
|
||||
void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
|
||||
{
|
||||
struct virtproc_info *vrp = ept->rpdev->vrp;
|
||||
|
||||
mutex_lock(&vrp->endpoints_lock);
|
||||
idr_remove(&vrp->endpoints, ept->addr);
|
||||
mutex_unlock(&vrp->endpoints_lock);
|
||||
|
||||
kfree(ept);
|
||||
__rpmsg_destroy_ept(ept->rpdev->vrp, ept);
|
||||
}
|
||||
EXPORT_SYMBOL(rpmsg_destroy_ept);
|
||||
|
||||
|
@ -764,6 +778,16 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
|
|||
print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1,
|
||||
msg, sizeof(*msg) + msg->len, true);
|
||||
|
||||
/*
|
||||
* We currently use fixed-sized buffers, so trivially sanitize
|
||||
* the reported payload length.
|
||||
*/
|
||||
if (len > RPMSG_BUF_SIZE ||
|
||||
msg->len > (len - sizeof(struct rpmsg_hdr))) {
|
||||
dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* use the dst addr to fetch the callback of the appropriate user */
|
||||
mutex_lock(&vrp->endpoints_lock);
|
||||
ept = idr_find(&vrp->endpoints, msg->dst);
|
||||
|
@ -774,7 +798,8 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
|
|||
else
|
||||
dev_warn(dev, "msg received with no recepient\n");
|
||||
|
||||
sg_init_one(&sg, msg, sizeof(*msg) + len);
|
||||
/* publish the real size of the buffer */
|
||||
sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
|
||||
|
||||
/* add the buffer back to the remote processor's virtqueue */
|
||||
err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
|
||||
|
@ -891,8 +916,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
|||
if (!bufs_va)
|
||||
goto vqs_del;
|
||||
|
||||
dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%x\n", bufs_va,
|
||||
vrp->bufs_dma);
|
||||
dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%llx\n", bufs_va,
|
||||
(unsigned long long)vrp->bufs_dma);
|
||||
|
||||
/* half of the buffers is dedicated for RX */
|
||||
vrp->rbufs = bufs_va;
|
||||
|
@ -964,6 +989,9 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev)
|
|||
if (ret)
|
||||
dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret);
|
||||
|
||||
if (vrp->ns_ept)
|
||||
__rpmsg_destroy_ept(vrp, vrp->ns_ept);
|
||||
|
||||
idr_remove_all(&vrp->endpoints);
|
||||
idr_destroy(&vrp->endpoints);
|
||||
|
||||
|
|
|
@ -41,48 +41,54 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/completion.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)
|
||||
#include <linux/idr.h>
|
||||
|
||||
/**
|
||||
* struct fw_resource - describes an entry from the resource section
|
||||
* @type: resource type
|
||||
* @id: index number of the resource
|
||||
* @da: device address of the resource
|
||||
* @pa: physical address of the resource
|
||||
* @len: size, in bytes, of the resource
|
||||
* @flags: properties of the resource, e.g. iommu protection required
|
||||
* @reserved: must be 0 atm
|
||||
* @name: name of resource
|
||||
* 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 remote processor firmware should contain a "resource table":
|
||||
* array of 'struct fw_resource' entries.
|
||||
* A resource table is essentially a list of system resources required
|
||||
* 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
|
||||
* of specific remoteproc configuration. Other entries require the host to
|
||||
* do something (e.g. reserve a requested resource) and possibly also reply
|
||||
* by overwriting a member inside 'struct fw_resource' with info about the
|
||||
* allocated resource.
|
||||
* do something (e.g. allocate a system resource). Sometimes a negotiation
|
||||
* is expected, where the firmware requests a resource, and once allocated,
|
||||
* the host should provide back its details (e.g. address of an allocated
|
||||
* memory region).
|
||||
*
|
||||
* 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 type will have its own structure.
|
||||
* 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.
|
||||
*
|
||||
* 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 id;
|
||||
u64 da;
|
||||
u64 pa;
|
||||
u32 len;
|
||||
u32 flags;
|
||||
u8 reserved[16];
|
||||
u8 name[48];
|
||||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
|
@ -92,30 +98,13 @@ struct fw_resource {
|
|||
* memory region.
|
||||
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
|
||||
* @RSC_TRACE: announces the availability of a trace buffer into which
|
||||
* the remote processor will be writing logs. In this case,
|
||||
* 'da' indicates the device address where logs are written to,
|
||||
* and 'len' is the size of the trace buffer.
|
||||
* @RSC_VRING: request for allocation of a virtio vring (address should
|
||||
* be indicated in 'da', and 'len' should contain the number
|
||||
* of buffers supported by the vring).
|
||||
* @RSC_VIRTIO_DEV: 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).
|
||||
* the remote processor will be writing logs.
|
||||
* @RSC_VDEV: declare support for a virtio device, and serve as its
|
||||
* virtio header.
|
||||
* @RSC_LAST: just keep this one at the end
|
||||
*
|
||||
* Most of the resource entries share the basic idea of address/length
|
||||
* negotiation with the host: the firmware usually asks (on behalf of the
|
||||
* 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).
|
||||
* For more details regarding a specific resource type, please see its
|
||||
* dedicated structure below.
|
||||
*
|
||||
* 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
|
||||
|
@ -126,11 +115,197 @@ enum fw_resource_type {
|
|||
RSC_CARVEOUT = 0,
|
||||
RSC_DEVMEM = 1,
|
||||
RSC_TRACE = 2,
|
||||
RSC_VRING = 3,
|
||||
RSC_VIRTIO_DEV = 4,
|
||||
RSC_LAST = 5,
|
||||
RSC_VDEV = 3,
|
||||
RSC_LAST = 4,
|
||||
};
|
||||
|
||||
#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
|
||||
* @va: virtual address
|
||||
|
@ -144,7 +319,7 @@ struct rproc_mem_entry {
|
|||
void *va;
|
||||
dma_addr_t dma;
|
||||
int len;
|
||||
u64 da;
|
||||
u32 da;
|
||||
void *priv;
|
||||
struct list_head node;
|
||||
};
|
||||
|
@ -206,7 +381,8 @@ enum rproc_state {
|
|||
* @mappings: list of iommu mappings we initiated, needed on shutdown
|
||||
* @firmware_loading_complete: marks e/o asynchronous firmware loading
|
||||
* @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 klist_node node;
|
||||
|
@ -226,24 +402,50 @@ struct rproc {
|
|||
struct list_head carveouts;
|
||||
struct list_head mappings;
|
||||
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 virtqueue *vq;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rproc_vdev - remoteproc state for a supported virtio device
|
||||
* @node: list node
|
||||
* @rproc: the rproc handle
|
||||
* @vdev: the virio device
|
||||
* @vq: the virtqueues for this vdev
|
||||
* @vring: the vrings for this vdev
|
||||
* @dfeatures: virtio device features
|
||||
* @gfeatures: virtio guest features
|
||||
*/
|
||||
struct rproc_vdev {
|
||||
struct list_head node;
|
||||
struct rproc *rproc;
|
||||
struct virtio_device vdev;
|
||||
struct virtqueue *vq[2];
|
||||
struct rproc_mem_entry vring[2];
|
||||
struct rproc_vring vring[RVDEV_NUM_VRINGS];
|
||||
unsigned long dfeatures;
|
||||
unsigned long gfeatures;
|
||||
};
|
||||
|
@ -261,9 +463,14 @@ int rproc_unregister(struct rproc *rproc);
|
|||
int rproc_boot(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)
|
||||
{
|
||||
struct rproc_vdev *rvdev = container_of(vdev, struct rproc_vdev, vdev);
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
|
||||
return rvdev->rproc;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue