remoteproc updates for v5.7
This introduces a range of improvements to the OMAP remoeteproc driver; among other things adding devicetree, suspend/resume and watchdog support, and adds support the remoteprocs in the DRA7xx SoC. It introduces support for 64-bit firmware, extends the ELF loader to support this and fixes for a number of race conditions in the recovery handling. It introduces a generic mechanism to allow remoteproc drivers to sync state with remote processors during a panic, and uses this to prepare Qualcomm remote processors for post mortem analysis. Finally it introduces fixes to cleanly recover from crashes in the modem firmware on production Qualcomm devices. -----BEGIN PGP SIGNATURE----- iQJPBAABCAA5FiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAl6FObEbHGJqb3JuLmFu ZGVyc3NvbkBsaW5hcm8ub3JnAAoJEAsfOT8Nma3FerQP/A4X/ONtMSwfamWseInj TRfYj5BOo6drGKavgG9NjQti2pUDfUo6wen906uRlgE48pEwToCVBZ1ERONj/pPD nsEzXWsZqDjVut9VH5Zhc+T9k+GJVlLkocu+EnpnS9uZ59ySuH/HFY0W6BoyVpuI 2ltodDq9TGHY3nFkaAz+yQKGsqB3e0RqlqCsTHnKHxeOsaG+HLyH89QPUIQzYMAJ P+CyW+h62KnlW7I0nteX3M86nmDxD1S7lQg6NK9GMiuOyVw4FyGiO3+AnX9JBue8 fFF1Z5344WT44IA7n/pjTdKtRy1R8CkwVKBzooWwpvGJo/3d3D1VcYN1S2IoNlb4 L3j3tBSexhthekZ5nBowHdfv5cbTAlWQn8GwKXDRLw4TVrV+wVdeG3kXNbhD6Ka2 sdAv5W4Eguap5dvlJqbC/d6cvQXiZOrzWogd0aqhSB6aMiyb+Pjol+kQZtNWIW1S CmvRFhlSnAwM0NLYQpXGzuaza8LvfMphG6+nwkneSD5IFclKQ+tUxmqOtt2dLqVG 9gtiqSt7fO58I4KxtQGWvp3FBa4FVeH00hF+H0Xu2B9pEsWYr9N2Ua5CDlIJdWKV OS6sVHlYB1a+E4IpJ92GEBp/KwmRJsVCNRKkbDE3Y7UNFoNoTPhnVlsdx5Sa/stT lTQPYoIKmOF8hTZEzLM4Vc7n =5Q77 -----END PGP SIGNATURE----- Merge tag 'rproc-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc Pull remoteproc updates from Bjorn Andersson: - a range of improvements to the OMAP remoeteproc driver; among other things adding devicetree, suspend/resume and watchdog support, and adds support the remoteprocs in the DRA7xx SoC - support for 64-bit firmware, extends the ELF loader to support this and fixes for a number of race conditions in the recovery handling - a generic mechanism to allow remoteproc drivers to sync state with remote processors during a panic, and uses this to prepare Qualcomm remote processors for post mortem analysis - fixes to cleanly recover from crashes in the modem firmware on production Qualcomm devices * tag 'rproc-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc: (37 commits) remoteproc/omap: Switch to SPDX license identifiers remoteproc/omap: Add watchdog functionality for remote processors remoteproc/omap: Report device exceptions and trigger recovery remoteproc/omap: Add support for runtime auto-suspend/resume remoteproc/omap: Add support for system suspend/resume remoteproc/omap: Request a timer(s) for remoteproc usage remoteproc/omap: Check for undefined mailbox messages remoteproc/omap: Remove the platform_data header remoteproc/omap: Add support for DRA7xx remote processors remoteproc/omap: Initialize and assign reserved memory node remoteproc/omap: Add the rproc ops .da_to_va() implementation remoteproc/omap: Add support to parse internal memories from DT remoteproc/omap: Add a sanity check for DSP boot address alignment remoteproc/omap: Add device tree support dt-bindings: remoteproc: Add OMAP remoteproc bindings remoteproc: qcom: Introduce panic handler for PAS and ADSP remoteproc: qcom: q6v5: Add common panic handler remoteproc: Introduce "panic" callback in ops remoteproc: Traverse rproc_list under RCU read lock remoteproc: Fix NULL pointer dereference in rproc_virtio_notify ...
This commit is contained in:
commit
c657011431
|
@ -0,0 +1,324 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/remoteproc/ti,omap-remoteproc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: OMAP4+ Remoteproc Devices
|
||||
|
||||
maintainers:
|
||||
- Suman Anna <s-anna@ti.com>
|
||||
|
||||
description:
|
||||
The OMAP family of SoCs usually have one or more slave processor sub-systems
|
||||
that are used to offload some of the processor-intensive tasks, or to manage
|
||||
other hardware accelerators, for achieving various system level goals.
|
||||
|
||||
The processor cores in the sub-system are usually behind an IOMMU, and may
|
||||
contain additional sub-modules like Internal RAM and/or ROMs, L1 and/or L2
|
||||
caches, an Interrupt Controller, a Cache Controller etc.
|
||||
|
||||
The OMAP SoCs usually have a DSP processor sub-system and/or an IPU processor
|
||||
sub-system. The DSP processor sub-system can contain any of the TI's C64x,
|
||||
C66x or C67x family of DSP cores as the main execution unit. The IPU processor
|
||||
sub-system usually contains either a Dual-Core Cortex-M3 or Dual-Core
|
||||
Cortex-M4 processors.
|
||||
|
||||
Each remote processor sub-system is represented as a single DT node. Each node
|
||||
has a number of required or optional properties that enable the OS running on
|
||||
the host processor (MPU) to perform the device management of the remote
|
||||
processor and to communicate with the remote processor. The various properties
|
||||
can be classified as constant or variable. The constant properties are
|
||||
dictated by the SoC and does not change from one board to another having the
|
||||
same SoC. Examples of constant properties include 'iommus', 'reg'. The
|
||||
variable properties are dictated by the system integration aspects such as
|
||||
memory on the board, or configuration used within the corresponding firmware
|
||||
image. Examples of variable properties include 'mboxes', 'memory-region',
|
||||
'timers', 'watchdog-timers' etc.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,omap4-dsp
|
||||
- ti,omap5-dsp
|
||||
- ti,dra7-dsp
|
||||
- ti,omap4-ipu
|
||||
- ti,omap5-ipu
|
||||
- ti,dra7-ipu
|
||||
|
||||
iommus:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description: |
|
||||
phandles to OMAP IOMMU nodes, that need to be programmed
|
||||
for this remote processor to access any external RAM memory or
|
||||
other peripheral device address spaces. This property usually
|
||||
has only a single phandle. Multiple phandles are used only in
|
||||
cases where the sub-system has different ports for different
|
||||
sub-modules within the processor sub-system (eg: DRA7 DSPs),
|
||||
and need the same programming in both the MMUs.
|
||||
|
||||
mboxes:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description: |
|
||||
OMAP Mailbox specifier denoting the sub-mailbox, to be used for
|
||||
communication with the remote processor. The specifier format is
|
||||
as per the bindings,
|
||||
Documentation/devicetree/bindings/mailbox/omap-mailbox.txt
|
||||
This property should match with the sub-mailbox node used in
|
||||
the firmware image.
|
||||
|
||||
clocks:
|
||||
description: |
|
||||
Main functional clock for the remote processor
|
||||
|
||||
resets:
|
||||
description: |
|
||||
Reset handles for the remote processor
|
||||
|
||||
firmware-name:
|
||||
description: |
|
||||
Default name of the firmware to load to the remote processor.
|
||||
|
||||
# Optional properties:
|
||||
# --------------------
|
||||
# Some of these properties are mandatory on some SoCs, and some are optional
|
||||
# depending on the configuration of the firmware image to be executed on the
|
||||
# remote processor. The conditions are mentioned for each property.
|
||||
#
|
||||
# The following are the optional properties:
|
||||
|
||||
memory-region:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: |
|
||||
phandle to the reserved memory node to be associated
|
||||
with the remoteproc device. The reserved memory node
|
||||
can be a CMA memory node, and should be defined as
|
||||
per the bindings,
|
||||
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
|
||||
|
||||
reg:
|
||||
description: |
|
||||
Address space for any remoteproc memories present on
|
||||
the SoC. Should contain an entry for each value in
|
||||
'reg-names'. These are mandatory for all DSP and IPU
|
||||
processors that have them (OMAP4/OMAP5 DSPs do not have
|
||||
any RAMs)
|
||||
|
||||
reg-names:
|
||||
description: |
|
||||
Required names for each of the address spaces defined in
|
||||
the 'reg' property. Expects the names from the following
|
||||
list, in the specified order, each representing the corresponding
|
||||
internal RAM memory region.
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
items:
|
||||
- const: l2ram
|
||||
- const: l1pram
|
||||
- const: l1dram
|
||||
|
||||
ti,bootreg:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: |
|
||||
Should be a triple of the phandle to the System Control
|
||||
Configuration region that contains the boot address
|
||||
register, the register offset of the boot address
|
||||
register within the System Control module, and the bit
|
||||
shift within the register. This property is required for
|
||||
all the DSP instances on OMAP4, OMAP5 and DRA7xx SoCs.
|
||||
|
||||
ti,autosuspend-delay-ms:
|
||||
description: |
|
||||
Custom autosuspend delay for the remoteproc in milliseconds.
|
||||
Recommended values is preferable to be in the order of couple
|
||||
of seconds. A negative value can also be used to disable the
|
||||
autosuspend behavior.
|
||||
|
||||
ti,timers:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: |
|
||||
One or more phandles to OMAP DMTimer nodes, that serve
|
||||
as System/Tick timers for the OS running on the remote
|
||||
processors. This will usually be a single timer if the
|
||||
processor sub-system is running in SMP mode, or one per
|
||||
core in the processor sub-system. This can also be used
|
||||
to reserve specific timers to be dedicated to the
|
||||
remote processors.
|
||||
|
||||
This property is mandatory on remote processors requiring
|
||||
external tick wakeup, and to support Power Management
|
||||
features. The timers to be used should match with the
|
||||
timers used in the firmware image.
|
||||
|
||||
ti,watchdog-timers:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: |
|
||||
One or more phandles to OMAP DMTimer nodes, used to
|
||||
serve as Watchdog timers for the processor cores. This
|
||||
will usually be one per executing processor core, even
|
||||
if the processor sub-system is running a SMP OS.
|
||||
|
||||
The timers to be used should match with the watchdog
|
||||
timers used in the firmware image.
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,dra7-dsp
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
required:
|
||||
- reg
|
||||
- reg-names
|
||||
- ti,bootreg
|
||||
|
||||
else:
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,omap4-ipu
|
||||
- ti,omap5-ipu
|
||||
- ti,dra7-ipu
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
ti,bootreg: false
|
||||
required:
|
||||
- reg
|
||||
- reg-names
|
||||
|
||||
else:
|
||||
properties:
|
||||
reg: false
|
||||
required:
|
||||
- ti,bootreg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- iommus
|
||||
- mboxes
|
||||
- clocks
|
||||
- resets
|
||||
- firmware-name
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
//Example 1: OMAP4 DSP
|
||||
|
||||
/* DSP Reserved Memory node */
|
||||
#include <dt-bindings/clock/omap4.h>
|
||||
reserved-memory {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
dsp_memory_region: dsp-memory@98000000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x98000000 0x800000>;
|
||||
reusable;
|
||||
};
|
||||
};
|
||||
|
||||
/* DSP node */
|
||||
ocp {
|
||||
dsp: dsp {
|
||||
compatible = "ti,omap4-dsp";
|
||||
ti,bootreg = <&scm_conf 0x304 0>;
|
||||
iommus = <&mmu_dsp>;
|
||||
mboxes = <&mailbox &mbox_dsp>;
|
||||
memory-region = <&dsp_memory_region>;
|
||||
ti,timers = <&timer5>;
|
||||
ti,watchdog-timers = <&timer6>;
|
||||
clocks = <&tesla_clkctrl OMAP4_DSP_CLKCTRL 0>;
|
||||
resets = <&prm_tesla 0>, <&prm_tesla 1>;
|
||||
firmware-name = "omap4-dsp-fw.xe64T";
|
||||
};
|
||||
};
|
||||
|
||||
- |+
|
||||
|
||||
//Example 2: OMAP5 IPU
|
||||
|
||||
/* IPU Reserved Memory node */
|
||||
#include <dt-bindings/clock/omap5.h>
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ipu_memory_region: ipu-memory@95800000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0 0x95800000 0 0x3800000>;
|
||||
reusable;
|
||||
};
|
||||
};
|
||||
|
||||
/* IPU node */
|
||||
ocp {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
ipu: ipu@55020000 {
|
||||
compatible = "ti,omap5-ipu";
|
||||
reg = <0x55020000 0x10000>;
|
||||
reg-names = "l2ram";
|
||||
iommus = <&mmu_ipu>;
|
||||
mboxes = <&mailbox &mbox_ipu>;
|
||||
memory-region = <&ipu_memory_region>;
|
||||
ti,timers = <&timer3>, <&timer4>;
|
||||
ti,watchdog-timers = <&timer9>, <&timer11>;
|
||||
clocks = <&ipu_clkctrl OMAP5_MMU_IPU_CLKCTRL 0>;
|
||||
resets = <&prm_core 2>;
|
||||
firmware-name = "omap5-ipu-fw.xem4";
|
||||
};
|
||||
};
|
||||
|
||||
- |+
|
||||
|
||||
//Example 3: DRA7xx/AM57xx DSP
|
||||
|
||||
/* DSP1 Reserved Memory node */
|
||||
#include <dt-bindings/clock/dra7.h>
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
dsp1_memory_region: dsp1-memory@99000000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x0 0x99000000 0x0 0x4000000>;
|
||||
reusable;
|
||||
};
|
||||
};
|
||||
|
||||
/* DSP1 node */
|
||||
ocp {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
dsp1: dsp@40800000 {
|
||||
compatible = "ti,dra7-dsp";
|
||||
reg = <0x40800000 0x48000>,
|
||||
<0x40e00000 0x8000>,
|
||||
<0x40f00000 0x8000>;
|
||||
reg-names = "l2ram", "l1pram", "l1dram";
|
||||
ti,bootreg = <&scm_conf 0x55c 0>;
|
||||
iommus = <&mmu0_dsp1>, <&mmu1_dsp1>;
|
||||
mboxes = <&mailbox5 &mbox_dsp1_ipc3x>;
|
||||
memory-region = <&dsp1_memory_region>;
|
||||
ti,timers = <&timer5>;
|
||||
ti,watchdog-timers = <&timer10>;
|
||||
resets = <&prm_dsp1 0>;
|
||||
clocks = <&dsp1_clkctrl DRA7_DSP1_MMU0_DSP1_CLKCTRL 0>;
|
||||
firmware-name = "dra7-dsp1-fw.xe66";
|
||||
};
|
||||
};
|
|
@ -230,7 +230,7 @@ in the used rings.
|
|||
Binary Firmware Structure
|
||||
=========================
|
||||
|
||||
At this point remoteproc only supports ELF32 firmware binaries. However,
|
||||
At this point remoteproc supports ELF32 and ELF64 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.
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ config MTK_SCP
|
|||
|
||||
config OMAP_REMOTEPROC
|
||||
tristate "OMAP remoteproc support"
|
||||
depends on ARCH_OMAP4 || SOC_OMAP5
|
||||
depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX
|
||||
depends on OMAP_IOMMU
|
||||
select MAILBOX
|
||||
select OMAP2PLUS_MBOX
|
||||
|
@ -52,6 +52,18 @@ config OMAP_REMOTEPROC
|
|||
It's safe to say N here if you're not interested in multimedia
|
||||
offloading or just want a bare minimum kernel.
|
||||
|
||||
config OMAP_REMOTEPROC_WATCHDOG
|
||||
bool "OMAP remoteproc watchdog timer"
|
||||
depends on OMAP_REMOTEPROC
|
||||
default n
|
||||
help
|
||||
Say Y here to enable watchdog timer for remote processors.
|
||||
|
||||
This option controls the watchdog functionality for the remote
|
||||
processors in OMAP. Dedicated OMAP DMTimers are used by the remote
|
||||
processors and triggers the timer interrupt upon a watchdog
|
||||
detection.
|
||||
|
||||
config WKUP_M3_RPROC
|
||||
tristate "AMx3xx Wakeup M3 remoteproc support"
|
||||
depends on SOC_AM33XX || SOC_AM43XX
|
||||
|
|
|
@ -186,7 +186,7 @@ static int imx_rproc_stop(struct rproc *rproc)
|
|||
}
|
||||
|
||||
static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
|
||||
int len, u64 *sys)
|
||||
size_t len, u64 *sys)
|
||||
{
|
||||
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
||||
int i;
|
||||
|
@ -203,19 +203,19 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
|
|||
}
|
||||
}
|
||||
|
||||
dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%x\n",
|
||||
dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%zx\n",
|
||||
da, len);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
void *va = NULL;
|
||||
u64 sys;
|
||||
int i;
|
||||
|
||||
if (len <= 0)
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
|
@ -235,7 +235,8 @@ static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
|||
}
|
||||
}
|
||||
|
||||
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va);
|
||||
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%p\n",
|
||||
da, len, va);
|
||||
|
||||
return va;
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ static void keystone_rproc_kick(struct rproc *rproc, int vqid)
|
|||
* can be used either by the remoteproc core for loading (when using kernel
|
||||
* remoteproc loader), or by any rpmsg bus drivers.
|
||||
*/
|
||||
static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct keystone_rproc *ksproc = rproc->priv;
|
||||
void __iomem *va = NULL;
|
||||
|
@ -255,7 +255,7 @@ static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
|||
size_t size;
|
||||
int i;
|
||||
|
||||
if (len <= 0)
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ksproc->num_mems; i++) {
|
||||
|
|
|
@ -320,7 +320,7 @@ stop:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void *scp_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *scp_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct mtk_scp *scp = (struct mtk_scp *)rproc->priv;
|
||||
int offset;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,35 +1,10 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
/*
|
||||
* Remote processor messaging
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011-2020 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name Texas Instruments nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _OMAP_RPMSG_H
|
||||
|
@ -56,6 +31,22 @@
|
|||
*
|
||||
* @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the
|
||||
* recovery mechanism (to some extent).
|
||||
*
|
||||
* @RP_MBOX_SUSPEND_AUTO: auto suspend request for the remote processor
|
||||
*
|
||||
* @RP_MBOX_SUSPEND_SYSTEM: system suspend request for the remote processor
|
||||
*
|
||||
* @RP_MBOX_SUSPEND_ACK: successful response from remote processor for a
|
||||
* suspend request
|
||||
*
|
||||
* @RP_MBOX_SUSPEND_CANCEL: a cancel suspend response from a remote processor
|
||||
* on a suspend request
|
||||
*
|
||||
* Introduce new message definitions if any here.
|
||||
*
|
||||
* @RP_MBOX_END_MSG: Indicates end of known/defined messages from remote core
|
||||
* This should be the last definition.
|
||||
*
|
||||
*/
|
||||
enum omap_rp_mbox_messages {
|
||||
RP_MBOX_READY = 0xFFFFFF00,
|
||||
|
@ -64,6 +55,11 @@ enum omap_rp_mbox_messages {
|
|||
RP_MBOX_ECHO_REQUEST = 0xFFFFFF03,
|
||||
RP_MBOX_ECHO_REPLY = 0xFFFFFF04,
|
||||
RP_MBOX_ABORT_REQUEST = 0xFFFFFF05,
|
||||
RP_MBOX_SUSPEND_AUTO = 0xFFFFFF10,
|
||||
RP_MBOX_SUSPEND_SYSTEM = 0xFFFFFF11,
|
||||
RP_MBOX_SUSPEND_ACK = 0xFFFFFF12,
|
||||
RP_MBOX_SUSPEND_CANCEL = 0xFFFFFF13,
|
||||
RP_MBOX_END_MSG = 0xFFFFFF14,
|
||||
};
|
||||
|
||||
#endif /* _OMAP_RPMSG_H */
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <linux/remoteproc.h>
|
||||
#include "qcom_q6v5.h"
|
||||
|
||||
#define Q6V5_PANIC_DELAY_MS 200
|
||||
|
||||
/**
|
||||
* qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start
|
||||
* @q6v5: reference to qcom_q6v5 context to be reinitialized
|
||||
|
@ -162,6 +164,24 @@ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop);
|
||||
|
||||
/**
|
||||
* qcom_q6v5_panic() - panic handler to invoke a stop on the remote
|
||||
* @q6v5: reference to qcom_q6v5 context
|
||||
*
|
||||
* Set the stop bit and sleep in order to allow the remote processor to flush
|
||||
* its caches etc for post mortem debugging.
|
||||
*
|
||||
* Return: 200ms
|
||||
*/
|
||||
unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5)
|
||||
{
|
||||
qcom_smem_state_update_bits(q6v5->state,
|
||||
BIT(q6v5->stop_bit), BIT(q6v5->stop_bit));
|
||||
|
||||
return Q6V5_PANIC_DELAY_MS;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_q6v5_panic);
|
||||
|
||||
/**
|
||||
* qcom_q6v5_init() - initializer of the q6v5 common struct
|
||||
* @q6v5: handle to be initialized
|
||||
|
|
|
@ -42,5 +42,6 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5);
|
|||
int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
|
||||
int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5);
|
||||
int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
|
||||
unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -270,7 +270,7 @@ static int adsp_stop(struct rproc *rproc)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
|
||||
int offset;
|
||||
|
@ -282,12 +282,20 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
|
|||
return adsp->mem_region + offset;
|
||||
}
|
||||
|
||||
static unsigned long adsp_panic(struct rproc *rproc)
|
||||
{
|
||||
struct qcom_adsp *adsp = rproc->priv;
|
||||
|
||||
return qcom_q6v5_panic(&adsp->q6v5);
|
||||
}
|
||||
|
||||
static const struct rproc_ops adsp_ops = {
|
||||
.start = adsp_start,
|
||||
.stop = adsp_stop,
|
||||
.da_to_va = adsp_da_to_va,
|
||||
.parse_fw = qcom_register_dump_segments,
|
||||
.load = adsp_load,
|
||||
.panic = adsp_panic,
|
||||
};
|
||||
|
||||
static int adsp_init_clock(struct qcom_adsp *adsp, const char **clk_ids)
|
||||
|
|
|
@ -381,23 +381,33 @@ static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds,
|
|||
}
|
||||
|
||||
static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm,
|
||||
bool remote_owner, phys_addr_t addr,
|
||||
bool local, bool remote, phys_addr_t addr,
|
||||
size_t size)
|
||||
{
|
||||
struct qcom_scm_vmperm next;
|
||||
struct qcom_scm_vmperm next[2];
|
||||
int perms = 0;
|
||||
|
||||
if (!qproc->need_mem_protection)
|
||||
return 0;
|
||||
if (remote_owner && *current_perm == BIT(QCOM_SCM_VMID_MSS_MSA))
|
||||
return 0;
|
||||
if (!remote_owner && *current_perm == BIT(QCOM_SCM_VMID_HLOS))
|
||||
|
||||
if (local == !!(*current_perm & BIT(QCOM_SCM_VMID_HLOS)) &&
|
||||
remote == !!(*current_perm & BIT(QCOM_SCM_VMID_MSS_MSA)))
|
||||
return 0;
|
||||
|
||||
next.vmid = remote_owner ? QCOM_SCM_VMID_MSS_MSA : QCOM_SCM_VMID_HLOS;
|
||||
next.perm = remote_owner ? QCOM_SCM_PERM_RW : QCOM_SCM_PERM_RWX;
|
||||
if (local) {
|
||||
next[perms].vmid = QCOM_SCM_VMID_HLOS;
|
||||
next[perms].perm = QCOM_SCM_PERM_RWX;
|
||||
perms++;
|
||||
}
|
||||
|
||||
if (remote) {
|
||||
next[perms].vmid = QCOM_SCM_VMID_MSS_MSA;
|
||||
next[perms].perm = QCOM_SCM_PERM_RW;
|
||||
perms++;
|
||||
}
|
||||
|
||||
return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K),
|
||||
current_perm, &next, 1);
|
||||
current_perm, next, perms);
|
||||
}
|
||||
|
||||
static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
|
||||
|
@ -803,7 +813,8 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw)
|
|||
|
||||
/* Hypervisor mapping to access metadata by modem */
|
||||
mdata_perm = BIT(QCOM_SCM_VMID_HLOS);
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, true, phys, size);
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, false, true,
|
||||
phys, size);
|
||||
if (ret) {
|
||||
dev_err(qproc->dev,
|
||||
"assigning Q6 access to metadata failed: %d\n", ret);
|
||||
|
@ -821,7 +832,8 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw)
|
|||
dev_err(qproc->dev, "MPSS header authentication failed: %d\n", ret);
|
||||
|
||||
/* Metadata authentication done, remove modem access */
|
||||
xferop_ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, false, phys, size);
|
||||
xferop_ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, true, false,
|
||||
phys, size);
|
||||
if (xferop_ret)
|
||||
dev_warn(qproc->dev,
|
||||
"mdt buffer not reclaimed system may become unstable\n");
|
||||
|
@ -908,7 +920,7 @@ static int q6v5_mba_load(struct q6v5 *qproc)
|
|||
}
|
||||
|
||||
/* Assign MBA image access in DDR to q6 */
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true,
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, true,
|
||||
qproc->mba_phys, qproc->mba_size);
|
||||
if (ret) {
|
||||
dev_err(qproc->dev,
|
||||
|
@ -945,8 +957,8 @@ halt_axi_ports:
|
|||
q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
|
||||
|
||||
reclaim_mba:
|
||||
xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false,
|
||||
qproc->mba_phys,
|
||||
xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true,
|
||||
false, qproc->mba_phys,
|
||||
qproc->mba_size);
|
||||
if (xfermemop_ret) {
|
||||
dev_err(qproc->dev,
|
||||
|
@ -1003,11 +1015,6 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
|
|||
writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
|
||||
}
|
||||
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm,
|
||||
false, qproc->mpss_phys,
|
||||
qproc->mpss_size);
|
||||
WARN_ON(ret);
|
||||
|
||||
q6v5_reset_assert(qproc);
|
||||
|
||||
q6v5_clk_disable(qproc->dev, qproc->reset_clks,
|
||||
|
@ -1021,7 +1028,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
|
|||
/* In case of failure or coredump scenario where reclaiming MBA memory
|
||||
* could not happen reclaim it here.
|
||||
*/
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false,
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, false,
|
||||
qproc->mba_phys,
|
||||
qproc->mba_size);
|
||||
WARN_ON(ret);
|
||||
|
@ -1037,6 +1044,23 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
|
|||
}
|
||||
}
|
||||
|
||||
static int q6v5_reload_mba(struct rproc *rproc)
|
||||
{
|
||||
struct q6v5 *qproc = rproc->priv;
|
||||
const struct firmware *fw;
|
||||
int ret;
|
||||
|
||||
ret = request_firmware(&fw, rproc->firmware, qproc->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
q6v5_load(rproc, fw);
|
||||
ret = q6v5_mba_load(qproc);
|
||||
release_firmware(fw);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int q6v5_mpss_load(struct q6v5 *qproc)
|
||||
{
|
||||
const struct elf32_phdr *phdrs;
|
||||
|
@ -1048,6 +1072,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
|
|||
phys_addr_t boot_addr;
|
||||
phys_addr_t min_addr = PHYS_ADDR_MAX;
|
||||
phys_addr_t max_addr = 0;
|
||||
u32 code_length;
|
||||
bool relocate = false;
|
||||
char *fw_name;
|
||||
size_t fw_name_len;
|
||||
|
@ -1097,6 +1122,24 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
|
|||
max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of a modem subsystem restart on secure devices, the modem
|
||||
* memory can be reclaimed only after MBA is loaded. For modem cold
|
||||
* boot this will be a nop
|
||||
*/
|
||||
q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, false,
|
||||
qproc->mpss_phys, qproc->mpss_size);
|
||||
|
||||
/* Share ownership between Linux and MSS, during segment loading */
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, true,
|
||||
qproc->mpss_phys, qproc->mpss_size);
|
||||
if (ret) {
|
||||
dev_err(qproc->dev,
|
||||
"assigning Q6 access to mpss memory failed: %d\n", ret);
|
||||
ret = -EAGAIN;
|
||||
goto release_firmware;
|
||||
}
|
||||
|
||||
mpss_reloc = relocate ? min_addr : qproc->mpss_phys;
|
||||
qproc->mpss_reloc = mpss_reloc;
|
||||
/* Load firmware segments */
|
||||
|
@ -1145,10 +1188,25 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
|
|||
phdr->p_memsz - phdr->p_filesz);
|
||||
}
|
||||
size += phdr->p_memsz;
|
||||
|
||||
code_length = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
|
||||
if (!code_length) {
|
||||
boot_addr = relocate ? qproc->mpss_phys : min_addr;
|
||||
writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG);
|
||||
writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
|
||||
}
|
||||
writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
|
||||
|
||||
ret = readl(qproc->rmb_base + RMB_MBA_STATUS_REG);
|
||||
if (ret < 0) {
|
||||
dev_err(qproc->dev, "MPSS authentication failed: %d\n",
|
||||
ret);
|
||||
goto release_firmware;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transfer ownership of modem ddr region to q6 */
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true,
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, false, true,
|
||||
qproc->mpss_phys, qproc->mpss_size);
|
||||
if (ret) {
|
||||
dev_err(qproc->dev,
|
||||
|
@ -1157,11 +1215,6 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
|
|||
goto release_firmware;
|
||||
}
|
||||
|
||||
boot_addr = relocate ? qproc->mpss_phys : min_addr;
|
||||
writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG);
|
||||
writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
|
||||
writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
|
||||
|
||||
ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_AUTH_COMPLETE, 10000);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_err(qproc->dev, "MPSS authentication timed out\n");
|
||||
|
@ -1186,8 +1239,16 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc,
|
|||
void *ptr = rproc_da_to_va(rproc, segment->da, segment->size);
|
||||
|
||||
/* Unlock mba before copying segments */
|
||||
if (!qproc->dump_mba_loaded)
|
||||
ret = q6v5_mba_load(qproc);
|
||||
if (!qproc->dump_mba_loaded) {
|
||||
ret = q6v5_reload_mba(rproc);
|
||||
if (!ret) {
|
||||
/* Reset ownership back to Linux to copy segments */
|
||||
ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm,
|
||||
true, false,
|
||||
qproc->mpss_phys,
|
||||
qproc->mpss_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ptr || ret)
|
||||
memset(dest, 0xff, segment->size);
|
||||
|
@ -1198,8 +1259,14 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc,
|
|||
|
||||
/* Reclaim mba after copying segments */
|
||||
if (qproc->dump_segment_mask == qproc->dump_complete_mask) {
|
||||
if (qproc->dump_mba_loaded)
|
||||
if (qproc->dump_mba_loaded) {
|
||||
/* Try to reset ownership back to Q6 */
|
||||
q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm,
|
||||
false, true,
|
||||
qproc->mpss_phys,
|
||||
qproc->mpss_size);
|
||||
q6v5_mba_reclaim(qproc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1225,8 +1292,8 @@ static int q6v5_start(struct rproc *rproc)
|
|||
goto reclaim_mpss;
|
||||
}
|
||||
|
||||
xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false,
|
||||
qproc->mba_phys,
|
||||
xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true,
|
||||
false, qproc->mba_phys,
|
||||
qproc->mba_size);
|
||||
if (xfermemop_ret)
|
||||
dev_err(qproc->dev,
|
||||
|
@ -1239,10 +1306,6 @@ static int q6v5_start(struct rproc *rproc)
|
|||
return 0;
|
||||
|
||||
reclaim_mpss:
|
||||
xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm,
|
||||
false, qproc->mpss_phys,
|
||||
qproc->mpss_size);
|
||||
WARN_ON(xfermemop_ret);
|
||||
q6v5_mba_reclaim(qproc);
|
||||
|
||||
return ret;
|
||||
|
@ -1264,7 +1327,7 @@ static int q6v5_stop(struct rproc *rproc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *q6v5_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct q6v5 *qproc = rproc->priv;
|
||||
int offset;
|
||||
|
|
|
@ -222,7 +222,7 @@ static int adsp_stop(struct rproc *rproc)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
|
||||
int offset;
|
||||
|
@ -234,12 +234,20 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
|
|||
return adsp->mem_region + offset;
|
||||
}
|
||||
|
||||
static unsigned long adsp_panic(struct rproc *rproc)
|
||||
{
|
||||
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
|
||||
|
||||
return qcom_q6v5_panic(&adsp->q6v5);
|
||||
}
|
||||
|
||||
static const struct rproc_ops adsp_ops = {
|
||||
.start = adsp_start,
|
||||
.stop = adsp_stop,
|
||||
.da_to_va = adsp_da_to_va,
|
||||
.parse_fw = qcom_register_dump_segments,
|
||||
.load = adsp_load,
|
||||
.panic = adsp_panic,
|
||||
};
|
||||
|
||||
static int adsp_init_clock(struct qcom_adsp *adsp)
|
||||
|
|
|
@ -406,7 +406,7 @@ static int q6v5_wcss_stop(struct rproc *rproc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct q6v5_wcss *wcss = rproc->priv;
|
||||
int offset;
|
||||
|
|
|
@ -287,7 +287,7 @@ static int wcnss_stop(struct rproc *rproc)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void *wcnss_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *wcnss_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
|
||||
int offset;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/devcoredump.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/idr.h>
|
||||
|
@ -38,11 +40,13 @@
|
|||
#include <linux/platform_device.h>
|
||||
|
||||
#include "remoteproc_internal.h"
|
||||
#include "remoteproc_elf_helpers.h"
|
||||
|
||||
#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL
|
||||
|
||||
static DEFINE_MUTEX(rproc_list_mutex);
|
||||
static LIST_HEAD(rproc_list);
|
||||
static struct notifier_block rproc_panic_nb;
|
||||
|
||||
typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
|
||||
void *, int offset, int avail);
|
||||
|
@ -185,7 +189,7 @@ EXPORT_SYMBOL(rproc_va_to_pa);
|
|||
* here the output of the DMA API for the carveouts, which should be more
|
||||
* correct.
|
||||
*/
|
||||
void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct rproc_mem_entry *carveout;
|
||||
void *ptr = NULL;
|
||||
|
@ -224,7 +228,8 @@ EXPORT_SYMBOL(rproc_da_to_va);
|
|||
/**
|
||||
* rproc_find_carveout_by_name() - lookup the carveout region by a name
|
||||
* @rproc: handle of a remote processor
|
||||
* @name,..: carveout name to find (standard printf format)
|
||||
* @name: carveout name to find (format string)
|
||||
* @...: optional parameters matching @name string
|
||||
*
|
||||
* Platform driver has the capability to register some pre-allacoted carveout
|
||||
* (physically contiguous memory regions) before rproc firmware loading and
|
||||
|
@ -318,8 +323,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
|
|||
struct device *dev = &rproc->dev;
|
||||
struct rproc_vring *rvring = &rvdev->vring[i];
|
||||
struct fw_rsc_vdev *rsc;
|
||||
int ret, size, notifyid;
|
||||
int ret, notifyid;
|
||||
struct rproc_mem_entry *mem;
|
||||
size_t size;
|
||||
|
||||
/* actual size of vring (in bytes) */
|
||||
size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
|
||||
|
@ -445,6 +451,7 @@ static void rproc_rvdev_release(struct device *dev)
|
|||
* rproc_handle_vdev() - handle a vdev fw resource
|
||||
* @rproc: the remote processor
|
||||
* @rsc: the vring resource descriptor
|
||||
* @offset: offset of the resource entry
|
||||
* @avail: size of available data (for sanity checking the image)
|
||||
*
|
||||
* This resource entry requests the host to statically register a virtio
|
||||
|
@ -587,6 +594,7 @@ void rproc_vdev_release(struct kref *ref)
|
|||
* rproc_handle_trace() - handle a shared trace buffer resource
|
||||
* @rproc: the remote processor
|
||||
* @rsc: the trace resource descriptor
|
||||
* @offset: offset of the resource entry
|
||||
* @avail: size of available data (for sanity checking the image)
|
||||
*
|
||||
* In case the remote processor dumps trace logs into memory,
|
||||
|
@ -652,6 +660,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
|
|||
* rproc_handle_devmem() - handle devmem resource entry
|
||||
* @rproc: remote processor handle
|
||||
* @rsc: the devmem resource entry
|
||||
* @offset: offset of the resource entry
|
||||
* @avail: size of available data (for sanity checking the image)
|
||||
*
|
||||
* Remote processors commonly need to access certain on-chip peripherals.
|
||||
|
@ -746,11 +755,12 @@ static int rproc_alloc_carveout(struct rproc *rproc,
|
|||
va = dma_alloc_coherent(dev->parent, mem->len, &dma, GFP_KERNEL);
|
||||
if (!va) {
|
||||
dev_err(dev->parent,
|
||||
"failed to allocate dma memory: len 0x%x\n", mem->len);
|
||||
"failed to allocate dma memory: len 0x%zx\n",
|
||||
mem->len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n",
|
||||
dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%zx\n",
|
||||
va, &dma, mem->len);
|
||||
|
||||
if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) {
|
||||
|
@ -853,6 +863,7 @@ static int rproc_release_carveout(struct rproc *rproc,
|
|||
* rproc_handle_carveout() - handle phys contig memory allocation requests
|
||||
* @rproc: rproc handle
|
||||
* @rsc: the resource entry
|
||||
* @offset: offset of the resource entry
|
||||
* @avail: size of available data (for image validation)
|
||||
*
|
||||
* This function will handle firmware requests for allocation of physically
|
||||
|
@ -957,7 +968,7 @@ EXPORT_SYMBOL(rproc_add_carveout);
|
|||
*/
|
||||
struct rproc_mem_entry *
|
||||
rproc_mem_entry_init(struct device *dev,
|
||||
void *va, dma_addr_t dma, int len, u32 da,
|
||||
void *va, dma_addr_t dma, size_t len, u32 da,
|
||||
int (*alloc)(struct rproc *, struct rproc_mem_entry *),
|
||||
int (*release)(struct rproc *, struct rproc_mem_entry *),
|
||||
const char *name, ...)
|
||||
|
@ -999,7 +1010,7 @@ EXPORT_SYMBOL(rproc_mem_entry_init);
|
|||
* provided by client.
|
||||
*/
|
||||
struct rproc_mem_entry *
|
||||
rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len,
|
||||
rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len,
|
||||
u32 da, const char *name, ...)
|
||||
{
|
||||
struct rproc_mem_entry *mem;
|
||||
|
@ -1022,7 +1033,7 @@ rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len,
|
|||
}
|
||||
EXPORT_SYMBOL(rproc_of_resm_mem_entry_init);
|
||||
|
||||
/**
|
||||
/*
|
||||
* A lookup table for resource handlers. The indices are defined in
|
||||
* enum fw_resource_type.
|
||||
*/
|
||||
|
@ -1270,7 +1281,7 @@ static void rproc_resource_cleanup(struct rproc *rproc)
|
|||
unmapped = iommu_unmap(rproc->domain, entry->da, entry->len);
|
||||
if (unmapped != entry->len) {
|
||||
/* nothing much to do besides complaining */
|
||||
dev_err(dev, "failed to unmap %u/%zu\n", entry->len,
|
||||
dev_err(dev, "failed to unmap %zx/%zu\n", entry->len,
|
||||
unmapped);
|
||||
}
|
||||
|
||||
|
@ -1564,20 +1575,21 @@ EXPORT_SYMBOL(rproc_coredump_add_custom_segment);
|
|||
static void rproc_coredump(struct rproc *rproc)
|
||||
{
|
||||
struct rproc_dump_segment *segment;
|
||||
struct elf32_phdr *phdr;
|
||||
struct elf32_hdr *ehdr;
|
||||
void *phdr;
|
||||
void *ehdr;
|
||||
size_t data_size;
|
||||
size_t offset;
|
||||
void *data;
|
||||
void *ptr;
|
||||
u8 class = rproc->elf_class;
|
||||
int phnum = 0;
|
||||
|
||||
if (list_empty(&rproc->dump_segments))
|
||||
return;
|
||||
|
||||
data_size = sizeof(*ehdr);
|
||||
data_size = elf_size_of_hdr(class);
|
||||
list_for_each_entry(segment, &rproc->dump_segments, node) {
|
||||
data_size += sizeof(*phdr) + segment->size;
|
||||
data_size += elf_size_of_phdr(class) + segment->size;
|
||||
|
||||
phnum++;
|
||||
}
|
||||
|
@ -1588,33 +1600,33 @@ static void rproc_coredump(struct rproc *rproc)
|
|||
|
||||
ehdr = data;
|
||||
|
||||
memset(ehdr, 0, sizeof(*ehdr));
|
||||
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
||||
ehdr->e_ident[EI_CLASS] = ELFCLASS32;
|
||||
ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
|
||||
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
|
||||
ehdr->e_type = ET_CORE;
|
||||
ehdr->e_machine = EM_NONE;
|
||||
ehdr->e_version = EV_CURRENT;
|
||||
ehdr->e_entry = rproc->bootaddr;
|
||||
ehdr->e_phoff = sizeof(*ehdr);
|
||||
ehdr->e_ehsize = sizeof(*ehdr);
|
||||
ehdr->e_phentsize = sizeof(*phdr);
|
||||
ehdr->e_phnum = phnum;
|
||||
memset(ehdr, 0, elf_size_of_hdr(class));
|
||||
/* e_ident field is common for both elf32 and elf64 */
|
||||
elf_hdr_init_ident(ehdr, class);
|
||||
|
||||
elf_hdr_set_e_type(class, ehdr, ET_CORE);
|
||||
elf_hdr_set_e_machine(class, ehdr, EM_NONE);
|
||||
elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
|
||||
elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
|
||||
elf_hdr_set_e_phoff(class, ehdr, elf_size_of_hdr(class));
|
||||
elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
|
||||
elf_hdr_set_e_phentsize(class, ehdr, elf_size_of_phdr(class));
|
||||
elf_hdr_set_e_phnum(class, ehdr, phnum);
|
||||
|
||||
phdr = data + elf_hdr_get_e_phoff(class, ehdr);
|
||||
offset = elf_hdr_get_e_phoff(class, ehdr);
|
||||
offset += elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr);
|
||||
|
||||
phdr = data + ehdr->e_phoff;
|
||||
offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum;
|
||||
list_for_each_entry(segment, &rproc->dump_segments, node) {
|
||||
memset(phdr, 0, sizeof(*phdr));
|
||||
phdr->p_type = PT_LOAD;
|
||||
phdr->p_offset = offset;
|
||||
phdr->p_vaddr = segment->da;
|
||||
phdr->p_paddr = segment->da;
|
||||
phdr->p_filesz = segment->size;
|
||||
phdr->p_memsz = segment->size;
|
||||
phdr->p_flags = PF_R | PF_W | PF_X;
|
||||
phdr->p_align = 0;
|
||||
memset(phdr, 0, elf_size_of_phdr(class));
|
||||
elf_phdr_set_p_type(class, phdr, PT_LOAD);
|
||||
elf_phdr_set_p_offset(class, phdr, offset);
|
||||
elf_phdr_set_p_vaddr(class, phdr, segment->da);
|
||||
elf_phdr_set_p_paddr(class, phdr, segment->da);
|
||||
elf_phdr_set_p_filesz(class, phdr, segment->size);
|
||||
elf_phdr_set_p_memsz(class, phdr, segment->size);
|
||||
elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X);
|
||||
elf_phdr_set_p_align(class, phdr, 0);
|
||||
|
||||
if (segment->dump) {
|
||||
segment->dump(rproc, segment, data + offset);
|
||||
|
@ -1630,8 +1642,8 @@ static void rproc_coredump(struct rproc *rproc)
|
|||
}
|
||||
}
|
||||
|
||||
offset += phdr->p_filesz;
|
||||
phdr++;
|
||||
offset += elf_phdr_get_p_filesz(class, phdr);
|
||||
phdr += elf_size_of_phdr(class);
|
||||
}
|
||||
|
||||
dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
|
||||
|
@ -1653,12 +1665,16 @@ int rproc_trigger_recovery(struct rproc *rproc)
|
|||
struct device *dev = &rproc->dev;
|
||||
int ret;
|
||||
|
||||
dev_err(dev, "recovering %s\n", rproc->name);
|
||||
|
||||
ret = mutex_lock_interruptible(&rproc->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* State could have changed before we got the mutex */
|
||||
if (rproc->state != RPROC_CRASHED)
|
||||
goto unlock_mutex;
|
||||
|
||||
dev_err(dev, "recovering %s\n", rproc->name);
|
||||
|
||||
ret = rproc_stop(rproc, true);
|
||||
if (ret)
|
||||
goto unlock_mutex;
|
||||
|
@ -1685,6 +1701,7 @@ unlock_mutex:
|
|||
|
||||
/**
|
||||
* rproc_crash_handler_work() - handle a crash
|
||||
* @work: work treating the crash
|
||||
*
|
||||
* This function needs to handle everything related to a crash, like cpu
|
||||
* registers and stack dump, information to help to debug the fatal error, etc.
|
||||
|
@ -1854,8 +1871,8 @@ struct rproc *rproc_get_by_phandle(phandle phandle)
|
|||
if (!np)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&rproc_list_mutex);
|
||||
list_for_each_entry(r, &rproc_list, node) {
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(r, &rproc_list, node) {
|
||||
if (r->dev.parent && r->dev.parent->of_node == np) {
|
||||
/* prevent underlying implementation from being removed */
|
||||
if (!try_module_get(r->dev.parent->driver->owner)) {
|
||||
|
@ -1868,7 +1885,7 @@ struct rproc *rproc_get_by_phandle(phandle phandle)
|
|||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&rproc_list_mutex);
|
||||
rcu_read_unlock();
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
|
@ -1925,7 +1942,7 @@ int rproc_add(struct rproc *rproc)
|
|||
|
||||
/* expose to rproc_get_by_phandle users */
|
||||
mutex_lock(&rproc_list_mutex);
|
||||
list_add(&rproc->node, &rproc_list);
|
||||
list_add_rcu(&rproc->node, &rproc_list);
|
||||
mutex_unlock(&rproc_list_mutex);
|
||||
|
||||
return 0;
|
||||
|
@ -2029,6 +2046,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
|
|||
rproc->name = name;
|
||||
rproc->priv = &rproc[1];
|
||||
rproc->auto_boot = true;
|
||||
rproc->elf_class = ELFCLASS32;
|
||||
|
||||
device_initialize(&rproc->dev);
|
||||
rproc->dev.parent = dev;
|
||||
|
@ -2053,7 +2071,8 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
|
|||
rproc->ops->load = rproc_elf_load_segments;
|
||||
rproc->ops->parse_fw = rproc_elf_load_rsc_table;
|
||||
rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
|
||||
rproc->ops->sanity_check = rproc_elf_sanity_check;
|
||||
if (!rproc->ops->sanity_check)
|
||||
rproc->ops->sanity_check = rproc_elf32_sanity_check;
|
||||
rproc->ops->get_boot_addr = rproc_elf_get_boot_addr;
|
||||
}
|
||||
|
||||
|
@ -2140,9 +2159,12 @@ int rproc_del(struct rproc *rproc)
|
|||
|
||||
/* the rproc is downref'ed as soon as it's removed from the klist */
|
||||
mutex_lock(&rproc_list_mutex);
|
||||
list_del(&rproc->node);
|
||||
list_del_rcu(&rproc->node);
|
||||
mutex_unlock(&rproc_list_mutex);
|
||||
|
||||
/* Ensure that no readers of rproc_list are still active */
|
||||
synchronize_rcu();
|
||||
|
||||
device_del(&rproc->dev);
|
||||
|
||||
return 0;
|
||||
|
@ -2216,10 +2238,50 @@ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
|
|||
}
|
||||
EXPORT_SYMBOL(rproc_report_crash);
|
||||
|
||||
static int rproc_panic_handler(struct notifier_block *nb, unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
unsigned int longest = 0;
|
||||
struct rproc *rproc;
|
||||
unsigned int d;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(rproc, &rproc_list, node) {
|
||||
if (!rproc->ops->panic || rproc->state != RPROC_RUNNING)
|
||||
continue;
|
||||
|
||||
d = rproc->ops->panic(rproc);
|
||||
longest = max(longest, d);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/*
|
||||
* Delay for the longest requested duration before returning. This can
|
||||
* be used by the remoteproc drivers to give the remote processor time
|
||||
* to perform any requested operations (such as flush caches), when
|
||||
* it's not possible to signal the Linux side due to the panic.
|
||||
*/
|
||||
mdelay(longest);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void __init rproc_init_panic(void)
|
||||
{
|
||||
rproc_panic_nb.notifier_call = rproc_panic_handler;
|
||||
atomic_notifier_chain_register(&panic_notifier_list, &rproc_panic_nb);
|
||||
}
|
||||
|
||||
static void __exit rproc_exit_panic(void)
|
||||
{
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list, &rproc_panic_nb);
|
||||
}
|
||||
|
||||
static int __init remoteproc_init(void)
|
||||
{
|
||||
rproc_init_sysfs();
|
||||
rproc_init_debugfs();
|
||||
rproc_init_panic();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2229,6 +2291,7 @@ static void __exit remoteproc_exit(void)
|
|||
{
|
||||
ida_destroy(&rproc_dev_index);
|
||||
|
||||
rproc_exit_panic();
|
||||
rproc_exit_debugfs();
|
||||
rproc_exit_sysfs();
|
||||
}
|
||||
|
|
|
@ -138,16 +138,16 @@ rproc_recovery_write(struct file *filp, const char __user *user_buf,
|
|||
buf[count - 1] = '\0';
|
||||
|
||||
if (!strncmp(buf, "enabled", count)) {
|
||||
/* change the flag and begin the recovery process if needed */
|
||||
rproc->recovery_disabled = false;
|
||||
/* if rproc has crashed, trigger recovery */
|
||||
if (rproc->state == RPROC_CRASHED)
|
||||
rproc_trigger_recovery(rproc);
|
||||
rproc_trigger_recovery(rproc);
|
||||
} else if (!strncmp(buf, "disabled", count)) {
|
||||
rproc->recovery_disabled = true;
|
||||
} else if (!strncmp(buf, "recover", count)) {
|
||||
/* if rproc has crashed, trigger recovery */
|
||||
if (rproc->state == RPROC_CRASHED)
|
||||
rproc_trigger_recovery(rproc);
|
||||
/* begin the recovery process without changing the flag */
|
||||
rproc_trigger_recovery(rproc);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
@ -293,7 +293,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p)
|
|||
seq_printf(seq, "\tVirtual address: %pK\n", carveout->va);
|
||||
seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma);
|
||||
seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da);
|
||||
seq_printf(seq, "\tLength: 0x%x Bytes\n\n", carveout->len);
|
||||
seq_printf(seq, "\tLength: 0x%zx Bytes\n\n", carveout->len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -349,7 +349,7 @@ void rproc_create_debug_dir(struct rproc *rproc)
|
|||
|
||||
debugfs_create_file("name", 0400, rproc->dbg_dir,
|
||||
rproc, &rproc_name_ops);
|
||||
debugfs_create_file("recovery", 0400, rproc->dbg_dir,
|
||||
debugfs_create_file("recovery", 0600, rproc->dbg_dir,
|
||||
rproc, &rproc_recovery_ops);
|
||||
debugfs_create_file("crash", 0200, rproc->dbg_dir,
|
||||
rproc, &rproc_crash_ops);
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Remote processor elf helpers defines
|
||||
*
|
||||
* Copyright (C) 2020 Kalray, Inc.
|
||||
*/
|
||||
|
||||
#ifndef REMOTEPROC_ELF_LOADER_H
|
||||
#define REMOTEPROC_ELF_LOADER_H
|
||||
|
||||
#include <linux/elf.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* fw_elf_get_class - Get elf class
|
||||
* @fw: the ELF firmware image
|
||||
*
|
||||
* Note that we use and elf32_hdr to access the class since the start of the
|
||||
* struct is the same for both elf class
|
||||
*
|
||||
* Return: elf class of the firmware
|
||||
*/
|
||||
static inline u8 fw_elf_get_class(const struct firmware *fw)
|
||||
{
|
||||
struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
|
||||
|
||||
return ehdr->e_ident[EI_CLASS];
|
||||
}
|
||||
|
||||
static inline void elf_hdr_init_ident(struct elf32_hdr *hdr, u8 class)
|
||||
{
|
||||
memcpy(hdr->e_ident, ELFMAG, SELFMAG);
|
||||
hdr->e_ident[EI_CLASS] = class;
|
||||
hdr->e_ident[EI_DATA] = ELFDATA2LSB;
|
||||
hdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
hdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
|
||||
}
|
||||
|
||||
/* Generate getter and setter for a specific elf struct/field */
|
||||
#define ELF_GEN_FIELD_GET_SET(__s, __field, __type) \
|
||||
static inline __type elf_##__s##_get_##__field(u8 class, const void *arg) \
|
||||
{ \
|
||||
if (class == ELFCLASS32) \
|
||||
return (__type) ((const struct elf32_##__s *) arg)->__field; \
|
||||
else \
|
||||
return (__type) ((const struct elf64_##__s *) arg)->__field; \
|
||||
} \
|
||||
static inline void elf_##__s##_set_##__field(u8 class, void *arg, \
|
||||
__type value) \
|
||||
{ \
|
||||
if (class == ELFCLASS32) \
|
||||
((struct elf32_##__s *) arg)->__field = (__type) value; \
|
||||
else \
|
||||
((struct elf64_##__s *) arg)->__field = (__type) value; \
|
||||
}
|
||||
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_entry, u64)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_phnum, u16)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_shnum, u16)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_phoff, u64)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_shoff, u64)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_shstrndx, u16)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_machine, u16)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_type, u16)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_version, u32)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_ehsize, u32)
|
||||
ELF_GEN_FIELD_GET_SET(hdr, e_phentsize, u16)
|
||||
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_paddr, u64)
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_vaddr, u64)
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_filesz, u64)
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_memsz, u64)
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_type, u32)
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_offset, u64)
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_flags, u32)
|
||||
ELF_GEN_FIELD_GET_SET(phdr, p_align, u64)
|
||||
|
||||
ELF_GEN_FIELD_GET_SET(shdr, sh_size, u64)
|
||||
ELF_GEN_FIELD_GET_SET(shdr, sh_offset, u64)
|
||||
ELF_GEN_FIELD_GET_SET(shdr, sh_name, u32)
|
||||
ELF_GEN_FIELD_GET_SET(shdr, sh_addr, u64)
|
||||
|
||||
#define ELF_STRUCT_SIZE(__s) \
|
||||
static inline unsigned long elf_size_of_##__s(u8 class) \
|
||||
{ \
|
||||
if (class == ELFCLASS32)\
|
||||
return sizeof(struct elf32_##__s); \
|
||||
else \
|
||||
return sizeof(struct elf64_##__s); \
|
||||
}
|
||||
|
||||
ELF_STRUCT_SIZE(shdr)
|
||||
ELF_STRUCT_SIZE(phdr)
|
||||
ELF_STRUCT_SIZE(hdr)
|
||||
|
||||
#endif /* REMOTEPROC_ELF_LOADER_H */
|
|
@ -23,20 +23,29 @@
|
|||
#include <linux/elf.h>
|
||||
|
||||
#include "remoteproc_internal.h"
|
||||
#include "remoteproc_elf_helpers.h"
|
||||
|
||||
/**
|
||||
* rproc_elf_sanity_check() - Sanity Check ELF firmware image
|
||||
* rproc_elf_sanity_check() - Sanity Check for ELF32/ELF64 firmware image
|
||||
* @rproc: the remote processor handle
|
||||
* @fw: the ELF firmware image
|
||||
*
|
||||
* Make sure this fw image is sane.
|
||||
* Make sure this fw image is sane (ie a correct ELF32/ELF64 file).
|
||||
*/
|
||||
int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
const char *name = rproc->firmware;
|
||||
struct device *dev = &rproc->dev;
|
||||
/*
|
||||
* Elf files are beginning with the same structure. Thus, to simplify
|
||||
* header parsing, we can use the elf32_hdr one for both elf64 and
|
||||
* elf32.
|
||||
*/
|
||||
struct elf32_hdr *ehdr;
|
||||
u32 elf_shdr_get_size;
|
||||
u64 phoff, shoff;
|
||||
char class;
|
||||
u16 phnum;
|
||||
|
||||
if (!fw) {
|
||||
dev_err(dev, "failed to load %s\n", name);
|
||||
|
@ -50,13 +59,22 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
|||
|
||||
ehdr = (struct elf32_hdr *)fw->data;
|
||||
|
||||
/* We only support ELF32 at this point */
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
|
||||
dev_err(dev, "Image is corrupted (bad magic)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
class = ehdr->e_ident[EI_CLASS];
|
||||
if (class != ELFCLASS32) {
|
||||
if (class != ELFCLASS32 && class != ELFCLASS64) {
|
||||
dev_err(dev, "Unsupported class: %d\n", class);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (class == ELFCLASS64 && fw->size < sizeof(struct elf64_hdr)) {
|
||||
dev_err(dev, "elf64 header is too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We assume the firmware has the same endianness as the host */
|
||||
# ifdef __LITTLE_ENDIAN
|
||||
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
|
@ -67,30 +85,54 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
|
||||
phoff = elf_hdr_get_e_phoff(class, fw->data);
|
||||
shoff = elf_hdr_get_e_shoff(class, fw->data);
|
||||
phnum = elf_hdr_get_e_phnum(class, fw->data);
|
||||
elf_shdr_get_size = elf_size_of_shdr(class);
|
||||
|
||||
if (fw->size < shoff + elf_shdr_get_size) {
|
||||
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) {
|
||||
if (phnum == 0) {
|
||||
dev_err(dev, "No loadable segments\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ehdr->e_phoff > fw->size) {
|
||||
if (phoff > fw->size) {
|
||||
dev_err(dev, "Firmware size is too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Firmware is an elf%d file\n",
|
||||
class == ELFCLASS32 ? 32 : 64);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rproc_elf_sanity_check);
|
||||
|
||||
/**
|
||||
* rproc_elf_sanity_check() - Sanity Check ELF32 firmware image
|
||||
* @rproc: the remote processor handle
|
||||
* @fw: the ELF32 firmware image
|
||||
*
|
||||
* Make sure this fw image is sane.
|
||||
*/
|
||||
int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
int ret = rproc_elf_sanity_check(rproc, fw);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (fw_elf_get_class(fw) == ELFCLASS32)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(rproc_elf32_sanity_check);
|
||||
|
||||
/**
|
||||
* rproc_elf_get_boot_addr() - Get rproc's boot address.
|
||||
* @rproc: the remote processor handle
|
||||
|
@ -102,11 +144,9 @@ EXPORT_SYMBOL(rproc_elf_sanity_check);
|
|||
* Note that the boot address is not a configurable property of all remote
|
||||
* processors. Some will always boot at a specific hard-coded address.
|
||||
*/
|
||||
u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
|
||||
u64 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;
|
||||
return elf_hdr_get_e_entry(fw_elf_get_class(fw), fw->data);
|
||||
}
|
||||
EXPORT_SYMBOL(rproc_elf_get_boot_addr);
|
||||
|
||||
|
@ -137,53 +177,65 @@ EXPORT_SYMBOL(rproc_elf_get_boot_addr);
|
|||
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;
|
||||
const void *ehdr, *phdr;
|
||||
int i, ret = 0;
|
||||
u16 phnum;
|
||||
const u8 *elf_data = fw->data;
|
||||
u8 class = fw_elf_get_class(fw);
|
||||
u32 elf_phdr_get_size = elf_size_of_phdr(class);
|
||||
|
||||
ehdr = (struct elf32_hdr *)elf_data;
|
||||
phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
|
||||
ehdr = elf_data;
|
||||
phnum = elf_hdr_get_e_phnum(class, ehdr);
|
||||
phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr);
|
||||
|
||||
/* 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;
|
||||
for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) {
|
||||
u64 da = elf_phdr_get_p_paddr(class, phdr);
|
||||
u64 memsz = elf_phdr_get_p_memsz(class, phdr);
|
||||
u64 filesz = elf_phdr_get_p_filesz(class, phdr);
|
||||
u64 offset = elf_phdr_get_p_offset(class, phdr);
|
||||
u32 type = elf_phdr_get_p_type(class, phdr);
|
||||
void *ptr;
|
||||
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
if (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);
|
||||
dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n",
|
||||
type, da, memsz, filesz);
|
||||
|
||||
if (filesz > memsz) {
|
||||
dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
|
||||
dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n",
|
||||
filesz, memsz);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset + filesz > fw->size) {
|
||||
dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
|
||||
dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n",
|
||||
offset + filesz, fw->size);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rproc_u64_fit_in_size_t(memsz)) {
|
||||
dev_err(dev, "size (%llx) does not fit in size_t type\n",
|
||||
memsz);
|
||||
ret = -EOVERFLOW;
|
||||
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);
|
||||
dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\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);
|
||||
if (filesz)
|
||||
memcpy(ptr, elf_data + offset, filesz);
|
||||
|
||||
/*
|
||||
* Zero out remaining memory for this segment.
|
||||
|
@ -196,28 +248,42 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
|
|||
memset(ptr + filesz, 0, memsz - filesz);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
rproc->elf_class = class;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(rproc_elf_load_segments);
|
||||
|
||||
static struct elf32_shdr *
|
||||
find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
|
||||
static const void *
|
||||
find_table(struct device *dev, const struct firmware *fw)
|
||||
{
|
||||
struct elf32_shdr *shdr;
|
||||
const void *shdr, *name_table_shdr;
|
||||
int i;
|
||||
const char *name_table;
|
||||
struct resource_table *table = NULL;
|
||||
const u8 *elf_data = (void *)ehdr;
|
||||
const u8 *elf_data = (void *)fw->data;
|
||||
u8 class = fw_elf_get_class(fw);
|
||||
size_t fw_size = fw->size;
|
||||
const void *ehdr = elf_data;
|
||||
u16 shnum = elf_hdr_get_e_shnum(class, ehdr);
|
||||
u32 elf_shdr_get_size = elf_size_of_shdr(class);
|
||||
u16 shstrndx = elf_hdr_get_e_shstrndx(class, ehdr);
|
||||
|
||||
/* look for the resource table and handle it */
|
||||
shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
|
||||
name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
|
||||
/* First, get the section header according to the elf class */
|
||||
shdr = elf_data + elf_hdr_get_e_shoff(class, ehdr);
|
||||
/* Compute name table section header entry in shdr array */
|
||||
name_table_shdr = shdr + (shstrndx * elf_shdr_get_size);
|
||||
/* Finally, compute the name table section address in elf */
|
||||
name_table = elf_data + elf_shdr_get_sh_offset(class, name_table_shdr);
|
||||
|
||||
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
|
||||
u32 size = shdr->sh_size;
|
||||
u32 offset = shdr->sh_offset;
|
||||
for (i = 0; i < shnum; i++, shdr += elf_shdr_get_size) {
|
||||
u64 size = elf_shdr_get_sh_size(class, shdr);
|
||||
u64 offset = elf_shdr_get_sh_offset(class, shdr);
|
||||
u32 name = elf_shdr_get_sh_name(class, shdr);
|
||||
|
||||
if (strcmp(name_table + shdr->sh_name, ".resource_table"))
|
||||
if (strcmp(name_table + name, ".resource_table"))
|
||||
continue;
|
||||
|
||||
table = (struct resource_table *)(elf_data + offset);
|
||||
|
@ -270,21 +336,21 @@ find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
|
|||
*/
|
||||
int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct elf32_hdr *ehdr;
|
||||
struct elf32_shdr *shdr;
|
||||
const void *shdr;
|
||||
struct device *dev = &rproc->dev;
|
||||
struct resource_table *table = NULL;
|
||||
const u8 *elf_data = fw->data;
|
||||
size_t tablesz;
|
||||
u8 class = fw_elf_get_class(fw);
|
||||
u64 sh_offset;
|
||||
|
||||
ehdr = (struct elf32_hdr *)elf_data;
|
||||
|
||||
shdr = find_table(dev, ehdr, fw->size);
|
||||
shdr = find_table(dev, fw);
|
||||
if (!shdr)
|
||||
return -EINVAL;
|
||||
|
||||
table = (struct resource_table *)(elf_data + shdr->sh_offset);
|
||||
tablesz = shdr->sh_size;
|
||||
sh_offset = elf_shdr_get_sh_offset(class, shdr);
|
||||
table = (struct resource_table *)(elf_data + sh_offset);
|
||||
tablesz = elf_shdr_get_sh_size(class, shdr);
|
||||
|
||||
/*
|
||||
* Create a copy of the resource table. When a virtio device starts
|
||||
|
@ -317,13 +383,24 @@ EXPORT_SYMBOL(rproc_elf_load_rsc_table);
|
|||
struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
|
||||
struct elf32_shdr *shdr;
|
||||
const void *shdr;
|
||||
u64 sh_addr, sh_size;
|
||||
u8 class = fw_elf_get_class(fw);
|
||||
struct device *dev = &rproc->dev;
|
||||
|
||||
shdr = find_table(&rproc->dev, ehdr, fw->size);
|
||||
shdr = find_table(&rproc->dev, fw);
|
||||
if (!shdr)
|
||||
return NULL;
|
||||
|
||||
return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
|
||||
sh_addr = elf_shdr_get_sh_addr(class, shdr);
|
||||
sh_size = elf_shdr_get_sh_size(class, shdr);
|
||||
|
||||
if (!rproc_u64_fit_in_size_t(sh_size)) {
|
||||
dev_err(dev, "size (%llx) does not fit in size_t type\n",
|
||||
sh_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rproc_da_to_va(rproc, sh_addr, sh_size);
|
||||
}
|
||||
EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);
|
||||
|
|
|
@ -50,12 +50,13 @@ void rproc_exit_sysfs(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);
|
||||
void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len);
|
||||
phys_addr_t rproc_va_to_pa(void *cpu_addr);
|
||||
int rproc_trigger_recovery(struct rproc *rproc);
|
||||
|
||||
int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw);
|
||||
int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw);
|
||||
u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw);
|
||||
u64 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw);
|
||||
int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw);
|
||||
int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw);
|
||||
struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
|
||||
|
@ -73,7 +74,7 @@ int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
|||
}
|
||||
|
||||
static inline
|
||||
u32 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
|
||||
u64 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
if (rproc->ops->get_boot_addr)
|
||||
return rproc->ops->get_boot_addr(rproc, fw);
|
||||
|
@ -119,4 +120,13 @@ struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
bool rproc_u64_fit_in_size_t(u64 val)
|
||||
{
|
||||
if (sizeof(size_t) == sizeof(u64))
|
||||
return true;
|
||||
|
||||
return (val <= (size_t) -1);
|
||||
}
|
||||
|
||||
#endif /* REMOTEPROC_INTERNAL_H */
|
||||
|
|
|
@ -320,6 +320,7 @@ static void rproc_virtio_dev_release(struct device *dev)
|
|||
/**
|
||||
* rproc_add_virtio_dev() - register an rproc-induced virtio device
|
||||
* @rvdev: the remote vdev
|
||||
* @id: the device type identification (used to match it with a driver).
|
||||
*
|
||||
* This function registers a virtio device. This vdev's partent is
|
||||
* the rproc device.
|
||||
|
@ -334,6 +335,13 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
|
|||
struct rproc_mem_entry *mem;
|
||||
int ret;
|
||||
|
||||
if (rproc->ops->kick == NULL) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, ".kick method not defined for %s",
|
||||
rproc->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to find dedicated vdev buffer carveout */
|
||||
mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index);
|
||||
if (mem) {
|
||||
|
|
|
@ -190,7 +190,7 @@ static int st_rproc_start(struct rproc *rproc)
|
|||
}
|
||||
}
|
||||
|
||||
dev_info(&rproc->dev, "Started from 0x%x\n", rproc->bootaddr);
|
||||
dev_info(&rproc->dev, "Started from 0x%llx\n", rproc->bootaddr);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -233,7 +233,7 @@ static const struct rproc_ops st_rproc_ops = {
|
|||
.parse_fw = st_rproc_parse_fw,
|
||||
.load = rproc_elf_load_segments,
|
||||
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.sanity_check = rproc_elf32_sanity_check,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr,
|
||||
};
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ static int slim_rproc_stop(struct rproc *rproc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct st_slim_rproc *slim_rproc = rproc->priv;
|
||||
void *va = NULL;
|
||||
|
@ -191,7 +191,7 @@ static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
|||
}
|
||||
}
|
||||
|
||||
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%pK\n",
|
||||
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%pK\n",
|
||||
da, len, va);
|
||||
|
||||
return va;
|
||||
|
@ -203,7 +203,7 @@ static const struct rproc_ops slim_rproc_ops = {
|
|||
.da_to_va = slim_rproc_da_to_va,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr,
|
||||
.load = rproc_elf_load_segments,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.sanity_check = rproc_elf32_sanity_check,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -505,7 +505,7 @@ static struct rproc_ops st_rproc_ops = {
|
|||
.load = rproc_elf_load_segments,
|
||||
.parse_fw = stm32_rproc_parse_fw,
|
||||
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.sanity_check = rproc_elf32_sanity_check,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr,
|
||||
};
|
||||
|
||||
|
@ -602,7 +602,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
|
|||
|
||||
err = stm32_rproc_get_syscon(np, "st,syscfg-pdds", &ddata->pdds);
|
||||
if (err)
|
||||
dev_warn(dev, "failed to get pdds\n");
|
||||
dev_info(dev, "failed to get pdds\n");
|
||||
|
||||
rproc->auto_boot = of_property_read_bool(np, "st,auto-boot");
|
||||
|
||||
|
|
|
@ -80,14 +80,14 @@ static int wkup_m3_rproc_stop(struct rproc *rproc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
||||
static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)
|
||||
{
|
||||
struct wkup_m3_rproc *wkupm3 = rproc->priv;
|
||||
void *va = NULL;
|
||||
int i;
|
||||
u32 offset;
|
||||
|
||||
if (len <= 0)
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < WKUPM3_MEM_MAX; i++) {
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Remote Processor - omap-specific bits
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _PLAT_REMOTEPROC_H
|
||||
#define _PLAT_REMOTEPROC_H
|
||||
|
||||
struct rproc_ops;
|
||||
struct platform_device;
|
||||
|
||||
/*
|
||||
* struct omap_rproc_pdata - omap remoteproc's platform data
|
||||
* @name: the remoteproc's name
|
||||
* @oh_name: omap hwmod device
|
||||
* @oh_name_opt: optional, secondary omap hwmod device
|
||||
* @firmware: name of firmware file to load
|
||||
* @mbox_name: name of omap mailbox device to use with this rproc
|
||||
* @ops: start/stop rproc handlers
|
||||
* @device_enable: omap-specific handler for enabling a device
|
||||
* @device_shutdown: omap-specific handler for shutting down a device
|
||||
* @set_bootaddr: omap-specific handler for setting the rproc boot address
|
||||
*/
|
||||
struct omap_rproc_pdata {
|
||||
const char *name;
|
||||
const char *oh_name;
|
||||
const char *oh_name_opt;
|
||||
const char *firmware;
|
||||
const char *mbox_name;
|
||||
const struct rproc_ops *ops;
|
||||
int (*device_enable)(struct platform_device *pdev);
|
||||
int (*device_shutdown)(struct platform_device *pdev);
|
||||
void (*set_bootaddr)(u32);
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OMAP_REMOTEPROC) || defined(CONFIG_OMAP_REMOTEPROC_MODULE)
|
||||
|
||||
void __init omap_rproc_reserve_cma(void);
|
||||
|
||||
#else
|
||||
|
||||
static inline void __init omap_rproc_reserve_cma(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _PLAT_REMOTEPROC_H */
|
|
@ -329,7 +329,7 @@ struct rproc;
|
|||
struct rproc_mem_entry {
|
||||
void *va;
|
||||
dma_addr_t dma;
|
||||
int len;
|
||||
size_t len;
|
||||
u32 da;
|
||||
void *priv;
|
||||
char name[32];
|
||||
|
@ -369,12 +369,14 @@ enum rsc_handling_status {
|
|||
* expects to find it
|
||||
* @sanity_check: sanity check the fw image
|
||||
* @get_boot_addr: get boot address to entry point specified in firmware
|
||||
* @panic: optional callback to react to system panic, core will delay
|
||||
* panic at least the returned number of milliseconds
|
||||
*/
|
||||
struct rproc_ops {
|
||||
int (*start)(struct rproc *rproc);
|
||||
int (*stop)(struct rproc *rproc);
|
||||
void (*kick)(struct rproc *rproc, int vqid);
|
||||
void * (*da_to_va)(struct rproc *rproc, u64 da, int len);
|
||||
void * (*da_to_va)(struct rproc *rproc, u64 da, size_t len);
|
||||
int (*parse_fw)(struct rproc *rproc, const struct firmware *fw);
|
||||
int (*handle_rsc)(struct rproc *rproc, u32 rsc_type, void *rsc,
|
||||
int offset, int avail);
|
||||
|
@ -382,7 +384,8 @@ struct rproc_ops {
|
|||
struct rproc *rproc, const struct firmware *fw);
|
||||
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);
|
||||
u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
|
||||
unsigned long (*panic)(struct rproc *rproc);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -498,7 +501,7 @@ struct rproc {
|
|||
int num_traces;
|
||||
struct list_head carveouts;
|
||||
struct list_head mappings;
|
||||
u32 bootaddr;
|
||||
u64 bootaddr;
|
||||
struct list_head rvdevs;
|
||||
struct list_head subdevs;
|
||||
struct idr notifyids;
|
||||
|
@ -514,6 +517,7 @@ struct rproc {
|
|||
bool auto_boot;
|
||||
struct list_head dump_segments;
|
||||
int nb_vdev;
|
||||
u8 elf_class;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -599,13 +603,13 @@ void rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem);
|
|||
|
||||
struct rproc_mem_entry *
|
||||
rproc_mem_entry_init(struct device *dev,
|
||||
void *va, dma_addr_t dma, int len, u32 da,
|
||||
void *va, dma_addr_t dma, size_t len, u32 da,
|
||||
int (*alloc)(struct rproc *, struct rproc_mem_entry *),
|
||||
int (*release)(struct rproc *, struct rproc_mem_entry *),
|
||||
const char *name, ...);
|
||||
|
||||
struct rproc_mem_entry *
|
||||
rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len,
|
||||
rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len,
|
||||
u32 da, const char *name, ...);
|
||||
|
||||
int rproc_boot(struct rproc *rproc);
|
||||
|
|
Loading…
Reference in New Issue