mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
A batch of remoteproc patches for 3.6:
- custom binary format support from Sjur Brændeland - groundwork for recovery and runtime pm support - some cleanups and API simplifications -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJQD75aAAoJELLolMlTRIoMM84P/RfnGhFl/mzKmDssFtwkajIb HRrZ1/YWjAjnSOvD5FF+LrS4TtxPgx9747I1Xh49Uk7DHb6siNTDUNbFJNifWo7g TbKo3LgLllfNXnwbunIfiGqOW2HzaCVo6vhk59fL3QAsdtCDlROegt1NbOrV8T2S +XZlgYhhUSlIQQCgsRBVJWbwP4k5PtAYmwL1VN7ONgj2ILJDP7MhAasDMl+KeGSG 128g+aCoIkfc9vC+ghDNAfE6DsHHeVyXAWPoWHoyjiteZ1NDkGxUBdTbsrtMYM2K ZguEISfVNcMSk10HhoYWtqYZfZUTEM18kOt/182CEwpRVRE34Z7fhcQCiCGX6u6v E+7tNj/0qow+dcj2OtS3NEePIHKcuvjBQ09b6GED+qsmC8lENo2Ly364T6JfriNl tv1PShvmodrBlLFusAikJKuzYFI9xgQawpL3oV0pMrEiujHqgNhuqrCQJkIWnA8d 9At2RZAMdFBFa7gd90lPicVqPR9HcGipVk7bKRyFAoqmpPLI85Nm1r9l4TPqEXpC Otb373gX30yqWNRD9Hmmxx/+40S2odELEDN4wrPRF+cCAbmWtFey415gdoaqBESn BiTRrHxNy2+dKsCgSUQMyn59rF552qrCb31REYAyMMImpftAaWP35OREFNn6oqj2 vzxsc4wcWZaLflrqQGj6 =vBgg -----END PGP SIGNATURE----- Merge tag 'remoteproc-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc Pull remoteproc update from Ohad Ben-Cohen: - custom binary format support from Sjur Brændeland - groundwork for recovery and runtime pm support - some cleanups and API simplifications Fix up conflicts in drivers/remoteproc/remoteproc_core.c due to clashes with earlier cleanups by Sjur Brændeland (with part of the cleanups moved into the new remoteproc_elf_loader.c file). * tag 'remoteproc-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc: MAINTAINERS: add remoteproc's git remoteproc: Support custom firmware handlers remoteproc: Move Elf related functions to separate file remoteproc: Add function rproc_get_boot_addr remoteproc: Pass struct fw to load_segments and find_rsc_table. remoteproc: adopt the driver core's alloc/add/del/put naming remoteproc: remove the get_by_name/put API remoteproc: support non-iommu carveout assignment remoteproc: simplify unregister/free interfaces remoteproc: remove the now-redundant kref remoteproc: maintain a generic child device for each rproc remoteproc: allocate vrings on demand, free when not needed
This commit is contained in:
commit
a9197f903f
@ -36,8 +36,7 @@ cost.
|
||||
Note: to use this function you should already have a valid rproc
|
||||
handle. There are several ways to achieve that cleanly (devres, pdata,
|
||||
the way remoteproc_rpmsg.c does this, or, if this becomes prevalent, we
|
||||
might also consider using dev_archdata for this). See also
|
||||
rproc_get_by_name() below.
|
||||
might also consider using dev_archdata for this).
|
||||
|
||||
void rproc_shutdown(struct rproc *rproc)
|
||||
- Power off a remote processor (previously booted with rproc_boot()).
|
||||
@ -51,30 +50,6 @@ cost.
|
||||
which means that the @rproc handle stays valid even after
|
||||
rproc_shutdown() returns, and users can still use it with a subsequent
|
||||
rproc_boot(), if needed.
|
||||
- don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly
|
||||
because rproc_shutdown() _does not_ decrement the refcount of @rproc.
|
||||
To decrement the refcount of @rproc, use rproc_put() (but _only_ if
|
||||
you acquired @rproc using rproc_get_by_name()).
|
||||
|
||||
struct rproc *rproc_get_by_name(const char *name)
|
||||
- Find an rproc handle using the remote processor's name, and then
|
||||
boot it. If it's already powered on, then just immediately return
|
||||
(successfully). Returns the rproc handle on success, and NULL on failure.
|
||||
This function increments the remote processor's refcount, so always
|
||||
use rproc_put() to decrement it back once rproc isn't needed anymore.
|
||||
Note: currently rproc_get_by_name() and rproc_put() are not used anymore
|
||||
by the rpmsg bus and its drivers. 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.
|
||||
|
||||
void rproc_put(struct rproc *rproc)
|
||||
- Decrement @rproc's power refcount and shut it down if it reaches zero
|
||||
(essentially by just calling rproc_shutdown), and then decrement @rproc's
|
||||
validity refcount too.
|
||||
After this function returns, @rproc may _not_ be used anymore, and its
|
||||
handle should be considered invalid.
|
||||
This function should be called _iff_ the @rproc handle was grabbed by
|
||||
calling rproc_get_by_name().
|
||||
|
||||
3. Typical usage
|
||||
|
||||
@ -115,21 +90,21 @@ int dummy_rproc_example(struct rproc *my_rproc)
|
||||
This function should be used by rproc implementations during
|
||||
initialization of the remote processor.
|
||||
After creating an rproc handle using this function, and when ready,
|
||||
implementations should then call rproc_register() to complete
|
||||
implementations should then call rproc_add() to complete
|
||||
the registration of the remote processor.
|
||||
On success, the new rproc is returned, and on failure, NULL.
|
||||
|
||||
Note: _never_ directly deallocate @rproc, even if it was not registered
|
||||
yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free().
|
||||
yet. Instead, when you need to unroll rproc_alloc(), use rproc_put().
|
||||
|
||||
void rproc_free(struct rproc *rproc)
|
||||
void rproc_put(struct rproc *rproc)
|
||||
- Free an rproc handle that was allocated by rproc_alloc.
|
||||
This function should _only_ be used if @rproc was only allocated,
|
||||
but not registered yet.
|
||||
If @rproc was already successfully registered (by calling
|
||||
rproc_register()), then use rproc_unregister() instead.
|
||||
This function essentially unrolls rproc_alloc(), by decrementing the
|
||||
rproc's refcount. It doesn't directly free rproc; that would happen
|
||||
only if there are no other references to rproc and its refcount now
|
||||
dropped to zero.
|
||||
|
||||
int rproc_register(struct rproc *rproc)
|
||||
int rproc_add(struct rproc *rproc)
|
||||
- Register @rproc with the remoteproc framework, after it has been
|
||||
allocated with rproc_alloc().
|
||||
This is called by the platform-specific rproc implementation, whenever
|
||||
@ -142,20 +117,15 @@ int dummy_rproc_example(struct rproc *my_rproc)
|
||||
of registering this remote processor, additional virtio drivers might get
|
||||
probed.
|
||||
|
||||
int rproc_unregister(struct rproc *rproc)
|
||||
- Unregister a remote processor, and decrement its refcount.
|
||||
If its refcount drops to zero, then @rproc will be freed. If not,
|
||||
it will be freed later once the last reference is dropped.
|
||||
|
||||
int rproc_del(struct rproc *rproc)
|
||||
- Unroll rproc_add().
|
||||
This function should be called when the platform specific rproc
|
||||
implementation decides to remove the rproc device. it should
|
||||
_only_ be called if a previous invocation of rproc_register()
|
||||
_only_ be called if a previous invocation of rproc_add()
|
||||
has completed successfully.
|
||||
|
||||
After rproc_unregister() returns, @rproc is _not_ valid anymore and
|
||||
it shouldn't be used. More specifically, don't call rproc_free()
|
||||
or try to directly free @rproc after rproc_unregister() returns;
|
||||
none of these are needed, and calling them is a bug.
|
||||
After rproc_del() returns, @rproc is still valid, and its
|
||||
last refcount should be decremented by calling rproc_put().
|
||||
|
||||
Returns 0 on success and -EINVAL if @rproc isn't valid.
|
||||
|
||||
|
@ -5725,6 +5725,7 @@ F: include/linux/regmap.h
|
||||
|
||||
REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM
|
||||
M: Ohad Ben-Cohen <ohad@wizery.com>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc.git
|
||||
S: Maintained
|
||||
F: drivers/remoteproc/
|
||||
F: Documentation/remoteproc.txt
|
||||
|
@ -6,4 +6,5 @@ obj-$(CONFIG_REMOTEPROC) += remoteproc.o
|
||||
remoteproc-y := remoteproc_core.o
|
||||
remoteproc-y += remoteproc_debugfs.o
|
||||
remoteproc-y += remoteproc_virtio.o
|
||||
remoteproc-y += remoteproc_elf_loader.o
|
||||
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
|
||||
|
@ -66,7 +66,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
|
||||
{
|
||||
mbox_msg_t msg = (mbox_msg_t) data;
|
||||
struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb);
|
||||
struct device *dev = oproc->rproc->dev;
|
||||
struct device *dev = oproc->rproc->dev.parent;
|
||||
const char *name = oproc->rproc->name;
|
||||
|
||||
dev_dbg(dev, "mbox msg: 0x%x\n", msg);
|
||||
@ -92,12 +92,13 @@ static int omap_rproc_mbox_callback(struct notifier_block *this,
|
||||
static void omap_rproc_kick(struct rproc *rproc, int vqid)
|
||||
{
|
||||
struct omap_rproc *oproc = rproc->priv;
|
||||
struct device *dev = rproc->dev.parent;
|
||||
int ret;
|
||||
|
||||
/* send the index of the triggered virtqueue in the mailbox payload */
|
||||
ret = omap_mbox_msg_send(oproc->mbox, vqid);
|
||||
if (ret)
|
||||
dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret);
|
||||
dev_err(dev, "omap_mbox_msg_send failed: %d\n", ret);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -110,7 +111,8 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid)
|
||||
static int omap_rproc_start(struct rproc *rproc)
|
||||
{
|
||||
struct omap_rproc *oproc = rproc->priv;
|
||||
struct platform_device *pdev = to_platform_device(rproc->dev);
|
||||
struct device *dev = rproc->dev.parent;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
@ -120,7 +122,7 @@ static int omap_rproc_start(struct rproc *rproc)
|
||||
oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb);
|
||||
if (IS_ERR(oproc->mbox)) {
|
||||
ret = PTR_ERR(oproc->mbox);
|
||||
dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret);
|
||||
dev_err(dev, "omap_mbox_get failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -133,13 +135,13 @@ static int omap_rproc_start(struct rproc *rproc)
|
||||
*/
|
||||
ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST);
|
||||
if (ret) {
|
||||
dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret);
|
||||
dev_err(dev, "omap_mbox_get failed: %d\n", ret);
|
||||
goto put_mbox;
|
||||
}
|
||||
|
||||
ret = pdata->device_enable(pdev);
|
||||
if (ret) {
|
||||
dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret);
|
||||
dev_err(dev, "omap_device_enable failed: %d\n", ret);
|
||||
goto put_mbox;
|
||||
}
|
||||
|
||||
@ -153,7 +155,8 @@ put_mbox:
|
||||
/* power off the remote processor */
|
||||
static int omap_rproc_stop(struct rproc *rproc)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(rproc->dev);
|
||||
struct device *dev = rproc->dev.parent;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
|
||||
struct omap_rproc *oproc = rproc->priv;
|
||||
int ret;
|
||||
@ -196,14 +199,14 @@ static int __devinit omap_rproc_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, rproc);
|
||||
|
||||
ret = rproc_register(rproc);
|
||||
ret = rproc_add(rproc);
|
||||
if (ret)
|
||||
goto free_rproc;
|
||||
|
||||
return 0;
|
||||
|
||||
free_rproc:
|
||||
rproc_free(rproc);
|
||||
rproc_put(rproc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -211,7 +214,10 @@ static int __devexit omap_rproc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rproc *rproc = platform_get_drvdata(pdev);
|
||||
|
||||
return rproc_unregister(rproc);
|
||||
rproc_del(rproc);
|
||||
rproc_put(rproc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap_rproc_driver = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -124,7 +124,7 @@ struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
|
||||
tfile = debugfs_create_file(name, 0400, rproc->dbg_dir,
|
||||
trace, &trace_rproc_ops);
|
||||
if (!tfile) {
|
||||
dev_err(rproc->dev, "failed to create debugfs trace entry\n");
|
||||
dev_err(&rproc->dev, "failed to create debugfs trace entry\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ void rproc_delete_debug_dir(struct rproc *rproc)
|
||||
|
||||
void rproc_create_debug_dir(struct rproc *rproc)
|
||||
{
|
||||
struct device *dev = rproc->dev;
|
||||
struct device *dev = &rproc->dev;
|
||||
|
||||
if (!rproc_dbg)
|
||||
return;
|
||||
|
295
drivers/remoteproc/remoteproc_elf_loader.c
Normal file
295
drivers/remoteproc/remoteproc_elf_loader.c
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Remote Processor Framework Elf loader
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*
|
||||
* Ohad Ben-Cohen <ohad@wizery.com>
|
||||
* Brian Swetland <swetland@google.com>
|
||||
* Mark Grosen <mgrosen@ti.com>
|
||||
* Fernando Guzman Lugo <fernando.lugo@ti.com>
|
||||
* Suman Anna <s-anna@ti.com>
|
||||
* Robert Tivy <rtivy@ti.com>
|
||||
* Armando Uribe De Leon <x0095078@ti.com>
|
||||
* Sjur Brændeland <sjur.brandeland@stericsson.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/elf.h>
|
||||
|
||||
#include "remoteproc_internal.h"
|
||||
|
||||
/**
|
||||
* rproc_elf_sanity_check() - Sanity Check ELF firmware image
|
||||
* @rproc: the remote processor handle
|
||||
* @fw: the ELF firmware image
|
||||
*
|
||||
* Make sure this fw image is sane.
|
||||
*/
|
||||
static int
|
||||
rproc_elf_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);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fw->size < sizeof(struct elf32_hdr)) {
|
||||
dev_err(dev, "Image is too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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) {
|
||||
# else /* BIG ENDIAN */
|
||||
if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
|
||||
# endif
|
||||
dev_err(dev, "Unsupported firmware endianess\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
|
||||
dev_err(dev, "Image is too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
|
||||
dev_err(dev, "Image is corrupted (bad magic)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ehdr->e_phnum == 0) {
|
||||
dev_err(dev, "No loadable segments\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ehdr->e_phoff > fw->size) {
|
||||
dev_err(dev, "Firmware size is too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_elf_get_boot_addr() - Get rproc's boot address.
|
||||
* @rproc: the remote processor handle
|
||||
* @fw: the ELF firmware image
|
||||
*
|
||||
* This function returns the entry point address of the ELF
|
||||
* image.
|
||||
*
|
||||
* Note that the boot address is not a configurable property of all remote
|
||||
* processors. Some will always boot at a specific hard-coded address.
|
||||
*/
|
||||
static
|
||||
u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
|
||||
|
||||
return ehdr->e_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_elf_load_segments() - load firmware segments to memory
|
||||
* @rproc: remote processor which will be booted using these fw segments
|
||||
* @fw: the ELF firmware image
|
||||
*
|
||||
* This function loads the firmware segments to memory, where the remote
|
||||
* processor expects them.
|
||||
*
|
||||
* Some remote processors will expect their code and data to be placed
|
||||
* in specific device addresses, and can't have them dynamically assigned.
|
||||
*
|
||||
* We currently support only those kind of remote processors, and expect
|
||||
* the program header's paddr member to contain those addresses. We then go
|
||||
* through the physically contiguous "carveout" memory regions which we
|
||||
* allocated (and mapped) earlier on behalf of the remote processor,
|
||||
* and "translate" device address to kernel addresses, so we can copy the
|
||||
* segments where they are expected.
|
||||
*
|
||||
* Currently we only support remote processors that required carveout
|
||||
* allocations and got them mapped onto their iommus. Some processors
|
||||
* might be different: they might not have iommus, and would prefer to
|
||||
* directly allocate memory for every segment/resource. This is not yet
|
||||
* supported, though.
|
||||
*/
|
||||
static int
|
||||
rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
struct elf32_hdr *ehdr;
|
||||
struct elf32_phdr *phdr;
|
||||
int i, ret = 0;
|
||||
const u8 *elf_data = fw->data;
|
||||
|
||||
ehdr = (struct elf32_hdr *)elf_data;
|
||||
phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
|
||||
|
||||
/* go through the available ELF segments */
|
||||
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
|
||||
u32 da = phdr->p_paddr;
|
||||
u32 memsz = phdr->p_memsz;
|
||||
u32 filesz = phdr->p_filesz;
|
||||
u32 offset = phdr->p_offset;
|
||||
void *ptr;
|
||||
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
|
||||
phdr->p_type, da, memsz, filesz);
|
||||
|
||||
if (filesz > memsz) {
|
||||
dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
|
||||
filesz, memsz);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset + filesz > fw->size) {
|
||||
dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
|
||||
offset + filesz, fw->size);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* grab the kernel address for this device address */
|
||||
ptr = rproc_da_to_va(rproc, da, memsz);
|
||||
if (!ptr) {
|
||||
dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* put the segment where the remote processor expects it */
|
||||
if (phdr->p_filesz)
|
||||
memcpy(ptr, elf_data + phdr->p_offset, filesz);
|
||||
|
||||
/*
|
||||
* Zero out remaining memory for this segment.
|
||||
*
|
||||
* This isn't strictly required since dma_alloc_coherent already
|
||||
* did this for us. albeit harmless, we may consider removing
|
||||
* this.
|
||||
*/
|
||||
if (memsz > filesz)
|
||||
memset(ptr + filesz, 0, memsz - filesz);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rproc_elf_find_rsc_table() - find the resource table
|
||||
* @rproc: the rproc handle
|
||||
* @fw: the ELF firmware image
|
||||
* @tablesz: place holder for providing back the table size
|
||||
*
|
||||
* This function finds the resource table inside the remote processor's
|
||||
* firmware. It is used both upon the registration of @rproc (in order
|
||||
* to look for and register the supported virito devices), and when the
|
||||
* @rproc is booted.
|
||||
*
|
||||
* Returns the pointer to the resource table if it is found, and write its
|
||||
* size into @tablesz. If a valid table isn't found, NULL is returned
|
||||
* (and @tablesz isn't set).
|
||||
*/
|
||||
static struct resource_table *
|
||||
rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
|
||||
int *tablesz)
|
||||
{
|
||||
struct elf32_hdr *ehdr;
|
||||
struct elf32_shdr *shdr;
|
||||
const char *name_table;
|
||||
struct device *dev = &rproc->dev;
|
||||
struct resource_table *table = NULL;
|
||||
int i;
|
||||
const u8 *elf_data = fw->data;
|
||||
|
||||
ehdr = (struct elf32_hdr *)elf_data;
|
||||
shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
|
||||
name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
|
||||
|
||||
/* look for the resource table and handle it */
|
||||
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
|
||||
int size = shdr->sh_size;
|
||||
int offset = shdr->sh_offset;
|
||||
|
||||
if (strcmp(name_table + shdr->sh_name, ".resource_table"))
|
||||
continue;
|
||||
|
||||
table = (struct resource_table *)(elf_data + offset);
|
||||
|
||||
/* make sure we have the entire table */
|
||||
if (offset + size > fw->size) {
|
||||
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 table;
|
||||
}
|
||||
|
||||
const struct rproc_fw_ops rproc_elf_fw_ops = {
|
||||
.load = rproc_elf_load_segments,
|
||||
.find_rsc_table = rproc_elf_find_rsc_table,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr
|
||||
};
|
@ -21,9 +21,27 @@
|
||||
#define REMOTEPROC_INTERNAL_H
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
struct rproc;
|
||||
|
||||
/**
|
||||
* struct rproc_fw_ops - firmware format specific operations.
|
||||
* @find_rsc_table: finds the resource table inside the firmware image
|
||||
* @load: load firmeware to memory, where the remote processor
|
||||
* expects to find it
|
||||
* @sanity_check: sanity check the fw image
|
||||
* @get_boot_addr: get boot address to entry point specified in firmware
|
||||
*/
|
||||
struct rproc_fw_ops {
|
||||
struct resource_table *(*find_rsc_table) (struct rproc *rproc,
|
||||
const struct firmware *fw,
|
||||
int *tablesz);
|
||||
int (*load)(struct rproc *rproc, const struct firmware *fw);
|
||||
int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
|
||||
u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
|
||||
};
|
||||
|
||||
/* from remoteproc_core.c */
|
||||
void rproc_release(struct kref *kref);
|
||||
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
|
||||
@ -41,4 +59,48 @@ void rproc_create_debug_dir(struct rproc *rproc);
|
||||
void rproc_init_debugfs(void);
|
||||
void rproc_exit_debugfs(void);
|
||||
|
||||
void rproc_free_vring(struct rproc_vring *rvring);
|
||||
int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
|
||||
|
||||
void *rproc_da_to_va(struct rproc *rproc, u64 da, int len);
|
||||
|
||||
static inline
|
||||
int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
if (rproc->fw_ops->sanity_check)
|
||||
return rproc->fw_ops->sanity_check(rproc, fw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
u32 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
if (rproc->fw_ops->get_boot_addr)
|
||||
return rproc->fw_ops->get_boot_addr(rproc, fw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
if (rproc->fw_ops->load)
|
||||
return rproc->fw_ops->load(rproc, fw);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct resource_table *rproc_find_rsc_table(struct rproc *rproc,
|
||||
const struct firmware *fw, int *tablesz)
|
||||
{
|
||||
if (rproc->fw_ops->find_rsc_table)
|
||||
return rproc->fw_ops->find_rsc_table(rproc, fw, tablesz);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern const struct rproc_fw_ops rproc_elf_fw_ops;
|
||||
|
||||
#endif /* REMOTEPROC_INTERNAL_H */
|
||||
|
@ -36,7 +36,7 @@ static void rproc_virtio_notify(struct virtqueue *vq)
|
||||
struct rproc *rproc = rvring->rvdev->rproc;
|
||||
int notifyid = rvring->notifyid;
|
||||
|
||||
dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid);
|
||||
dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);
|
||||
|
||||
rproc->ops->kick(rproc, notifyid);
|
||||
}
|
||||
@ -57,7 +57,7 @@ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
|
||||
{
|
||||
struct rproc_vring *rvring;
|
||||
|
||||
dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid);
|
||||
dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
|
||||
|
||||
rvring = idr_find(&rproc->notifyids, notifyid);
|
||||
if (!rvring || !rvring->vq)
|
||||
@ -74,17 +74,21 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
|
||||
{
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct rproc *rproc = vdev_to_rproc(vdev);
|
||||
struct device *dev = &rproc->dev;
|
||||
struct rproc_vring *rvring;
|
||||
struct virtqueue *vq;
|
||||
void *addr;
|
||||
int len, size;
|
||||
int len, size, ret;
|
||||
|
||||
/* we're temporarily limited to two virtqueues per rvdev */
|
||||
if (id >= ARRAY_SIZE(rvdev->vring))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
rvring = &rvdev->vring[id];
|
||||
ret = rproc_alloc_vring(rvdev, id);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
rvring = &rvdev->vring[id];
|
||||
addr = rvring->va;
|
||||
len = rvring->len;
|
||||
|
||||
@ -92,7 +96,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
|
||||
size = vring_size(len, rvring->align);
|
||||
memset(addr, 0, size);
|
||||
|
||||
dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n",
|
||||
dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n",
|
||||
id, addr, len, rvring->notifyid);
|
||||
|
||||
/*
|
||||
@ -102,7 +106,8 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
|
||||
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);
|
||||
dev_err(dev, "vring_new_virtqueue %s failed\n", name);
|
||||
rproc_free_vring(rvring);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
@ -125,6 +130,7 @@ static void rproc_virtio_del_vqs(struct virtio_device *vdev)
|
||||
rvring = vq->priv;
|
||||
rvring->vq = NULL;
|
||||
vring_del_virtqueue(vq);
|
||||
rproc_free_vring(rvring);
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +153,7 @@ 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);
|
||||
dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -219,7 +225,7 @@ static struct virtio_config_ops rproc_virtio_config_ops = {
|
||||
|
||||
/*
|
||||
* This function is called whenever vdev is released, and is responsible
|
||||
* to decrement the remote processor's refcount taken when vdev was
|
||||
* to decrement the remote processor's refcount which was taken when vdev was
|
||||
* added.
|
||||
*
|
||||
* Never call this function directly; it will be called by the driver
|
||||
@ -228,9 +234,13 @@ static struct virtio_config_ops rproc_virtio_config_ops = {
|
||||
static void rproc_vdev_release(struct device *dev)
|
||||
{
|
||||
struct virtio_device *vdev = dev_to_virtio(dev);
|
||||
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
|
||||
struct rproc *rproc = vdev_to_rproc(vdev);
|
||||
|
||||
kref_put(&rproc->refcount, rproc_release);
|
||||
list_del(&rvdev->node);
|
||||
kfree(rvdev);
|
||||
|
||||
put_device(&rproc->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,7 +255,7 @@ static void rproc_vdev_release(struct device *dev)
|
||||
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 virtio_device *vdev = &rvdev->vdev;
|
||||
int ret;
|
||||
|
||||
@ -262,11 +272,11 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
|
||||
* Therefore we must increment the rproc refcount here, and decrement
|
||||
* it _only_ when the vdev is released.
|
||||
*/
|
||||
kref_get(&rproc->refcount);
|
||||
get_device(&rproc->dev);
|
||||
|
||||
ret = register_virtio_device(vdev);
|
||||
if (ret) {
|
||||
kref_put(&rproc->refcount, rproc_release);
|
||||
put_device(&rproc->dev);
|
||||
dev_err(dev, "failed to register vdev: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
@ -956,7 +956,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
||||
vrp->svq = vqs[1];
|
||||
|
||||
/* allocate coherent memory for the buffers */
|
||||
bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
|
||||
bufs_va = dma_alloc_coherent(vdev->dev.parent->parent,
|
||||
RPMSG_TOTAL_BUF_SPACE,
|
||||
&vrp->bufs_dma, GFP_KERNEL);
|
||||
if (!bufs_va)
|
||||
goto vqs_del;
|
||||
|
@ -36,7 +36,6 @@
|
||||
#define REMOTEPROC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/klist.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/virtio.h>
|
||||
@ -369,8 +368,8 @@ enum rproc_state {
|
||||
* @firmware: name of firmware file to be loaded
|
||||
* @priv: private data which belongs to the platform-specific rproc module
|
||||
* @ops: platform-specific start/stop rproc handlers
|
||||
* @dev: underlying device
|
||||
* @refcount: refcount of users that have a valid pointer to this rproc
|
||||
* @dev: virtual device for refcounting and common remoteproc behavior
|
||||
* @fw_ops: firmware-specific handlers
|
||||
* @power: refcount of users who need this rproc powered up
|
||||
* @state: state of the device
|
||||
* @lock: lock which protects concurrent manipulations of the rproc
|
||||
@ -383,6 +382,7 @@ enum rproc_state {
|
||||
* @bootaddr: address of first instruction to boot rproc with (optional)
|
||||
* @rvdevs: list of remote virtio devices
|
||||
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids
|
||||
* @index: index of this rproc device
|
||||
*/
|
||||
struct rproc {
|
||||
struct klist_node node;
|
||||
@ -391,8 +391,8 @@ struct rproc {
|
||||
const char *firmware;
|
||||
void *priv;
|
||||
const struct rproc_ops *ops;
|
||||
struct device *dev;
|
||||
struct kref refcount;
|
||||
struct device dev;
|
||||
const struct rproc_fw_ops *fw_ops;
|
||||
atomic_t power;
|
||||
unsigned int state;
|
||||
struct mutex lock;
|
||||
@ -405,6 +405,7 @@ struct rproc {
|
||||
u32 bootaddr;
|
||||
struct list_head rvdevs;
|
||||
struct idr notifyids;
|
||||
int index;
|
||||
};
|
||||
|
||||
/* we currently support only two vrings per rvdev */
|
||||
@ -450,15 +451,12 @@ struct rproc_vdev {
|
||||
unsigned long gfeatures;
|
||||
};
|
||||
|
||||
struct rproc *rproc_get_by_name(const char *name);
|
||||
void rproc_put(struct rproc *rproc);
|
||||
|
||||
struct rproc *rproc_alloc(struct device *dev, const char *name,
|
||||
const struct rproc_ops *ops,
|
||||
const char *firmware, int len);
|
||||
void rproc_free(struct rproc *rproc);
|
||||
int rproc_register(struct rproc *rproc);
|
||||
int rproc_unregister(struct rproc *rproc);
|
||||
void rproc_put(struct rproc *rproc);
|
||||
int rproc_add(struct rproc *rproc);
|
||||
int rproc_del(struct rproc *rproc);
|
||||
|
||||
int rproc_boot(struct rproc *rproc);
|
||||
void rproc_shutdown(struct rproc *rproc);
|
||||
|
Loading…
Reference in New Issue
Block a user