[PATCH 1/7] amp/remoteproc: add framework for controlling remote processors
Jean-Christophe PLAGNIOL-VILLARD
plagnioj at jcrosoft.com
Wed Oct 26 01:16:04 EDT 2011
On 11:48 Tue 25 Oct , Ohad Ben-Cohen wrote:
> Modern SoCs typically employ a central symmetric multiprocessing (SMP)
> application processor running Linux, with several other asymmetric
> multiprocessing (AMP) heterogeneous processors running different instances
> of operating system, whether Linux or any other flavor of real-time OS.
>
> Booting a remote processor in an AMP configuration typically involves:
> - Loading a firmware which contains the OS image
> - Allocating and providing it required system resources (e.g. memory)
> - Programming an IOMMU (when relevant)
> - Powering on the device
>
> This patch introduces a generic framework that allows drivers to do
> that. In the future, this framework will also include runtime power
> management and error recovery.
>
> Based on (but now quite far from) work done by Fernando Guzman Lugo
> <fernando.lugo at ti.com>.
>
> ELF loader was written by Mark Grosen <mgrosen at ti.com>, based on
> msm's Peripheral Image Loader (PIL) by Stephen Boyd <sboyd at codeaurora.org>.
>
> Designed with Brian Swetland <swetland at google.com>.
>
> Signed-off-by: Ohad Ben-Cohen <ohad at wizery.com>
> Cc: Brian Swetland <swetland at google.com>
> Cc: Arnd Bergmann <arnd at arndb.de>
> Cc: Grant Likely <grant.likely at secretlab.ca>
> Cc: Tony Lindgren <tony at atomide.com>
> Cc: Russell King <linux at arm.linux.org.uk>
> Cc: Rusty Russell <rusty at rustcorp.com.au>
> Cc: Andrew Morton <akpm at linux-foundation.org>
> Cc: Greg KH <greg at kroah.com>
> Cc: Stephen Boyd <sboyd at codeaurora.org>
> ---
> Documentation/amp/remoteproc.txt | 324 ++++++
> MAINTAINERS | 7 +
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/amp/Kconfig | 9 +
> drivers/amp/Makefile | 1 +
> drivers/amp/remoteproc/Kconfig | 3 +
> drivers/amp/remoteproc/Makefile | 6 +
> drivers/amp/remoteproc/remoteproc_core.c | 1410 ++++++++++++++++++++++++++
> drivers/amp/remoteproc/remoteproc_internal.h | 44 +
> include/linux/amp/remoteproc.h | 265 +++++
> 11 files changed, 2072 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/amp/remoteproc.txt
> create mode 100644 drivers/amp/Kconfig
> create mode 100644 drivers/amp/Makefile
> create mode 100644 drivers/amp/remoteproc/Kconfig
> create mode 100644 drivers/amp/remoteproc/Makefile
> create mode 100644 drivers/amp/remoteproc/remoteproc_core.c
> create mode 100644 drivers/amp/remoteproc/remoteproc_internal.h
> create mode 100644 include/linux/amp/remoteproc.h
>
> diff --git a/Documentation/amp/remoteproc.txt b/Documentation/amp/remoteproc.txt
> new file mode 100644
> index 0000000..63cecd9
> --- /dev/null
> +++ b/Documentation/amp/remoteproc.txt
> @@ -0,0 +1,324 @@
> +Remote Processor Framework
> +
> +1. Introduction
> +
> +Modern SoCs typically have heterogeneous remote processor devices in asymmetric
> +multiprocessing (AMP) configurations, which may be running different instances
> +of operating system, whether it's Linux or any other flavor of real-time OS.
> +
> +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
> +In a typical configuration, the dual cortex-A9 is running Linux in a SMP
> +configuration, and each of the other three cores (two M3 cores and a DSP)
> +is running its own instance of RTOS in an AMP configuration.
> +
> +The remoteproc framework allows different platforms/architectures to
> +control (power on, load firmware, power off) those remote processors while
> +abstracting the hardware differences, so the entire driver doesn't need to be
> +duplicated. In addition, this framework also adds rpmsg virtio devices
> +for remote processors that supports this kind of communication. This way,
> +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/amp/rpmsg.txt).
> +
> +2. User API
> +
> + int rproc_boot(struct rproc *rproc)
> + - Boot a remote processor (i.e. load its firmware, power it on, ...).
> + If the remote processor is already powered on, this function immediately
> + returns (successfully).
> + Returns 0 on success, and an appropriate error value otherwise.
> + 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.
> +
> + void rproc_shutdown(struct rproc *rproc)
> + - Power off a remote processor (previously booted with rproc_boot()).
> + In case @rproc is still being used by an additional user(s), then
> + this function will just decrement the power refcount and exit,
> + without really powering off the device.
> + Every call to rproc_boot() must (eventually) be accompanied by a call
> + to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug.
> + Notes:
> + - we're not decrementing the rproc's refcount, only the power refcount.
> + 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 this function (and its counterpart rproc_put()) are not
> + used anymore by the amp sub-system. 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
> +
> +#include <linux/amp/remoteproc.h>
> +
> +/* in case we were given a valid 'rproc' handle */
> +int dummy_rproc_example(struct rproc *my_rproc)
> +{
> + int ret;
> +
> + /* let's power on and boot our remote processor */
> + ret = rproc_boot(my_rproc);
> + if (ret) {
> + /*
> + * something went wrong. handle it and leave.
> + */
> + }
> +
> + /*
> + * our remote processor is now powered on... give it some work
> + */
> +
> + /* let's shut it down now */
> + rproc_shutdown(my_rproc);
> +}
> +
> +4. API for implementors
> +
> + struct rproc *rproc_alloc(struct device *dev, const char *name,
> + const struct rproc_ops *ops,
> + const char *firmware, int len)
> + - Allocate a new remote processor handle, but don't register
> + it yet. Required parameters are the underlying device, the
> + name of this remote processor, platform-specific ops handlers,
> + the name of the firmware to boot this rproc with, and the
> + length of private data needed by the allocating rproc driver (in bytes).
> +
> + 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
> + 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().
> +
> + void rproc_free(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.
> +
> + int rproc_register(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
> + a new remote processor device is probed.
> + Returns 0 on success and an appropriate error code otherwise.
> + Note: this function initiates an asynchronous firmware loading
> + context, which will look for virtio devices supported by the rproc's
> + firmware.
> + 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.
> + If its refcount drops to zero, then @rproc will be freed. If not,
> + it will be freed later once the last reference is dropped.
> +
> + 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()
> + 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.
> +
> + Returns 0 on success and -EINVAL if @rproc isn't valid.
> +
> +5. Implementation callbacks
> +
> +These callbacks should be provided by platform-specific remoteproc
> +drivers:
> +
> +/**
> + * struct rproc_ops - platform-specific device handlers
> + * @start: power on the device and boot it
> + * @stop: power off the device
> + * @kick: kick a virtqueue (virtqueue id given as a parameter)
> + */
> +struct rproc_ops {
> + int (*start)(struct rproc *rproc);
> + int (*stop)(struct rproc *rproc);
> + void (*kick)(struct rproc *rproc, int vqid);
> +};
> +
> +Every remoteproc implementation should at least provide the ->start and ->stop
> +handlers. If rpmsg 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
> +device and boot it (use rproc->priv to access platform-specific private data).
> +The boot address, in case needed, can be found in rproc->bootaddr (remoteproc
> +core puts there the ELF entry point).
> +On success, 0 should be returned, and on failure, an appropriate error code.
> +
> +The ->stop() handler takes an rproc handle and powers the device down.
> +On success, 0 is returned, and on failure, an appropriate error code.
> +
> +The ->kick() handler takes an rproc handle, and an index of a virtqueue
> +where new message was placed in. Implementations should interrupt the remote
> +processor and let it know it has pending messages. Notifying remote processors
> +the exact virtqueue index to look in is optional: it is easy (and not
> +too expensive) to go through the existing virtqueues and look for new buffers
> +in the used rings.
> +
> +6. Binary Firmware Structure
> +
> +At this point remoteproc only supports ELF32 firmware binaries. However,
> +it is quite expected that other platforms/devices which we'd want to
> +support with this framework will be based on different binary formats.
> +
> +When those use cases show up, we will have to decouple the binary format
> +from the framework core, so we can support several binary formats without
> +duplicating common code.
> +
> +When the firmware is parsed, its various segments are loaded to memory
> +according to the specified device address (might be a physical address
> +if the remote processor is accessing memory directly).
> +
> +In addition to the standard ELF segments, most remote processors would
> +also include a special section which we call "the resource table".
> +
> +The resource table contains system resources that the remote processor
> +requires before it should be powered on, such as allocation of physically
> +contiguous memory, or iommu mapping of certain on-chip peripherals.
> +Remotecore will only power up the device after all the resource table's
> +requirement are met.
> +
> +In addition to system resources, the resource table may also contain
> +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:
> +
> +/**
> + * 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 fw_resource {
> + u32 type;
> + u32 id;
> + u64 da;
> + u64 pa;
> + u32 len;
> + u32 flags;
> + u8 reserved[16];
> + u8 name[48];
> +} __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.
> +
> +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:
> +
> +/**
> + * enum fw_resource_type - types of resource entries
> + *
> + * @RSC_CARVEOUT: request for allocation of a physically contiguous
> + * 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).
> + */
> +enum fw_resource_type {
> + RSC_CARVEOUT = 0,
> + RSC_DEVMEM = 1,
> + RSC_TRACE = 2,
> + RSC_VRING = 3,
> + RSC_VIRTIO_DEV = 4,
> + RSC_VIRTIO_CFG = 5,
> +};
> +
> +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).
> +
> +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
> +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.
> +
> +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.
> +
> +Of course, RSC_VIRTIO 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).
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ace8f9c..2812cd7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1205,6 +1205,13 @@ L: lm-sensors at lm-sensors.org
> S: Maintained
> F: drivers/hwmon/asb100.c
>
> +ASYNCHRONOUS MULTIPROCESSING (AMP) FRAMEWORK
> +M: Ohad Ben-Cohen <ohad at wizery.com>
> +S: Maintained
> +F: drivers/amp/
> +F: Documentation/amp/
> +F: include/linux/amp/
> +
> ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
> M: Dan Williams <dan.j.williams at intel.com>
> W: http://sourceforge.net/projects/xscaleiop
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 95b9e7e..dfb7d36 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -128,6 +128,8 @@ source "drivers/clocksource/Kconfig"
>
> source "drivers/iommu/Kconfig"
>
> +source "drivers/amp/Kconfig"
> +
> source "drivers/virt/Kconfig"
>
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 7fa433a..8f41a77 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -124,6 +124,7 @@ obj-y += clk/
> obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
> obj-$(CONFIG_NFC) += nfc/
> obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
> +obj-y += amp/
>
> # Virtualization drivers
> obj-$(CONFIG_VIRT_DRIVERS) += virt/
> diff --git a/drivers/amp/Kconfig b/drivers/amp/Kconfig
> new file mode 100644
> index 0000000..23a8ed1
> --- /dev/null
> +++ b/drivers/amp/Kconfig
> @@ -0,0 +1,9 @@
> +#
> +# AMP subsystem configuration
> +#
> +
> +menu "Asymmetric Multiprocessing (AMP) Framework"
> +
> +source "drivers/amp/remoteproc/Kconfig"
> +
> +endmenu
> diff --git a/drivers/amp/Makefile b/drivers/amp/Makefile
> new file mode 100644
> index 0000000..708461d
> --- /dev/null
> +++ b/drivers/amp/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_REMOTEPROC) += remoteproc/
> diff --git a/drivers/amp/remoteproc/Kconfig b/drivers/amp/remoteproc/Kconfig
> new file mode 100644
> index 0000000..b250b15
> --- /dev/null
> +++ b/drivers/amp/remoteproc/Kconfig
> @@ -0,0 +1,3 @@
> +# REMOTEPROC gets selected by whoever wants it
> +config REMOTEPROC
> + tristate
> diff --git a/drivers/amp/remoteproc/Makefile b/drivers/amp/remoteproc/Makefile
> new file mode 100644
> index 0000000..2a5fd79
> --- /dev/null
> +++ b/drivers/amp/remoteproc/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Generic framework for controlling remote processors
> +#
> +
> +obj-$(CONFIG_REMOTEPROC) += remoteproc.o
> +remoteproc-y := remoteproc_core.o
> diff --git a/drivers/amp/remoteproc/remoteproc_core.c b/drivers/amp/remoteproc/remoteproc_core.c
> new file mode 100644
> index 0000000..be6774f
> --- /dev/null
> +++ b/drivers/amp/remoteproc/remoteproc_core.c
> @@ -0,0 +1,1410 @@
> +/*
> + * Remote Processor Framework
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + * Copyright (C) 2011 Google, Inc.
> + *
> + * Ohad Ben-Cohen <ohad at wizery.com>
> + * Brian Swetland <swetland at google.com>
> + * Mark Grosen <mgrosen at ti.com>
> + * Fernando Guzman Lugo <fernando.lugo at ti.com>
> + * Suman Anna <s-anna at ti.com>
> + * Robert Tivy <rtivy at ti.com>
> + * Armando Uribe De Leon <x0095078 at ti.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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/firmware.h>
> +#include <linux/string.h>
> +#include <linux/debugfs.h>
> +#include <linux/amp/remoteproc.h>
> +#include <linux/iommu.h>
> +#include <linux/klist.h>
> +#include <linux/elf.h>
> +#include <linux/virtio_ids.h>
> +#include <linux/virtio_ring.h>
> +
> +#include "remoteproc_internal.h"
> +
> +static void klist_rproc_get(struct klist_node *n);
> +static void klist_rproc_put(struct klist_node *n);
> +
> +/*
> + * klist of the available remote processors.
> + *
> + * 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 amp
> + * framework. The use cases that do require its existence should be
> + * scrutinized, and hopefully migrated to rproc_boot() using device-based
> + * binding.
> + *
> + * If/when this materializes, we could drop the klist (and the by_name
> + * API).
> + */
> +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);
> +
> +/*
> + * This is the IOMMU fault handler we register with the IOMMU API
> + * (when relevant; not all remote processors access memory through
> + * an IOMMU).
> + *
> + * IOMMU core will invoke this handler whenever the remote processor
> + * will try to access an unmapped device address.
> + *
> + * Currently this is mostly a stub, but it will be later used to trigger
> + * the recovery of the remote processor.
> + */
> +static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
> + unsigned long iova, int flags)
> +{
> + dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags);
> +
> + /*
> + * Let the iommu core know we're not really handling this fault;
> + * we just plan to use this as a recovery trigger.
> + */
> + return -ENOSYS;
> +}
> +
> +static int rproc_enable_iommu(struct rproc *rproc)
> +{
> + struct iommu_domain *domain;
> + struct device *dev = rproc->dev;
> + int ret;
> +
> + /*
> + * We currently use iommu_present() to decide if an IOMMU
> + * setup is needed.
> + *
> + * This works for simple cases, but will easily fail with
> + * platforms that do have an IOMMU, but not for this specific
> + * rproc.
> + *
> + * This will be easily solved by introducing hw capabilities
> + * that will be set by the remoteproc driver.
> + */
> + if (!iommu_present(dev->bus)) {
> + dev_err(dev, "iommu not found\n");
> + return -ENODEV;
> + }
> +
> + domain = iommu_domain_alloc(dev->bus);
> + if (!domain) {
> + dev_err(dev, "can't alloc iommu domain\n");
> + return -ENOMEM;
> + }
> +
> + iommu_set_fault_handler(domain, rproc_iommu_fault);
> +
> + ret = iommu_attach_device(domain, dev);
> + if (ret) {
> + dev_err(dev, "can't attach iommu device: %d\n", ret);
> + goto free_domain;
> + }
> +
> + rproc->domain = domain;
> +
> + return 0;
> +
> +free_domain:
> + iommu_domain_free(domain);
> + return ret;
> +}
> +
> +static void rproc_disable_iommu(struct rproc *rproc)
> +{
> + struct iommu_domain *domain = rproc->domain;
> + struct device *dev = rproc->dev;
> +
> + if (!domain)
> + return;
> +
> + iommu_detach_device(domain, dev);
> + iommu_domain_free(domain);
> +
> + return;
> +}
> +
> +/*
> + * Some remote processors will ask us to allocate them physically contiguous
> + * memory regions (which we call "carveouts"), and map them to specific
> + * device addresses (which are hardcoded in the firmware).
> + *
> + * They may then ask us to copy objects into specific device addresses (e.g.
> + * code/data sections) or expose us certain symbols in other device address
> + * (e.g. their trace buffer).
> + *
> + * This function is an internal helper with which we can go over the allocated
> + * carveouts and translate specific device address to kernel virtual addresses
> + * so we can access the referenced memory.
> + *
> + * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too,
> + * but only on kernel direct mapped RAM memory. Instead, we're just using
> + * here the output of the DMA API, which should be more correct.
> + */
> +static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
> +{
> + struct rproc_mem_entry *carveout;
> + void *ptr = NULL;
> +
> + list_for_each_entry(carveout, &rproc->carveouts, node) {
> + int offset = da - carveout->da;
> +
> + /* try next carveout if da is too small */
> + if (offset < 0)
> + continue;
> +
> + /* try next carveout if da is too large */
> + if (offset + len > carveout->len)
> + continue;
> +
> + ptr = carveout->va + offset;
> +
> + break;
> + }
> +
> + return ptr;
> +}
> +
> +/**
> + * rproc_load_segments() - load firmware segments to memory
> + * @rproc: remote processor which will be booted using these fw segments
> + * @elf_data: the content of 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.
On STM Soc you can upload the firmware code where you want
the you just have to specify in 2 register where the entry point of the co-pro
we need to support this too
the elf can be self relocated, I used barebox this way.
BTW do you plan to support CMA as a lot of copro will request a big
contineuous memory to work on between the cpus.
I've to go to the ELCE will continue the review later
Best Regards,
J.
More information about the linux-arm-kernel
mailing list