mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-24 10:11:26 +03:00
Import tegra platform drivers from linux-4.4. These files
are introduced or touched by the following commits in
linux-4.4:
5798930 arm: mach-tegra: Get rid of apbio.{c,h}
618d424 platform: tegra: enable denver_serr only for t18x
c9f681e arm: tegra: get rid of nct header
14cb7cf platform: tegra: nvdumper: Remove code which need NCT
46adb21 platform: tegra: mc: make mc-sid driver scalable and upstreamable
b0ea9ac usb: phy: Move header files to include/linux
7d63191 platform: tegra: mc: allow modules to access mc registers
1d5ac46 soc/tegra: Add GPU EDP Management
09166fd soc/tegra: Add CPU EDP Management
7118c16 platform: powergate: tegra: update ISPA name
59cebf1 bwmgr: Add tegra21 specific bwmgr functionality
e91ada2 bwmgr: merge dram specific functionality to common code
19fb2fe drivers: platform: tegra: remove TEGRA_21x defconfig
b87f6739 platform: nvadsp: fix is_mboxq_full check
b11cdba platform: tegra: powergate: do not handle SATA clk
8324487 platform: tegra: mc: remove cl34000094 hacks
a77d415 platform: tegra: mc: rename mc-sid.c to tegra-mc-sid.c
df6d5a8 platform: tegra: remove support for soctherm
e96ddb9 Merge "Merge branch 'dev/praithatha/k4.4_mc_sid' into dev-kernel-4.4" into dev-kernel-4.4
2f69aa5 Merge branch 'dev/praithatha/k4.4_mc_sid' into dev-kernel-4.4
e171bb1 adsp: dfs: override adsp dfs and reinit actmon
b7ca294 platform: tegra: mc: Add streamID configuration
457e747 nvdumper: fixed Coverity defects
e12c0c0 platform: tegra: Use ARCH_TEGRA_APE for APE PD code
40eba0d platform: tegra: drop flowctrl code
568ad6e central actmon: fix extra interrupt
944bdbf clk: tegra: Correct some nvenc clock names
5c957fe driver: platform: tegra: GR support
5af5163 coverity: Fix the error reported by Coverity
576ea23 tegra-virt-alt: Add adsp virtualization support
7407552 platform: tegra: nvadsp: Fix coverity defect
d11b6a0 platform: tegra: nvadsp: Fix coverity defect
6c41ef0 tegra: nvadsp: fix compilation when CONFIG_PM=n
4262e32 adsp: Add virtualization check
85384e2 Fix an uninitialized local value error
44a8d9c tegra: mc: export symbol tegra_set_latency_allowance
f699ce7 platform: nvadsp: disable app loading for secload
5ed4b72 drivers: tegra: allow to get "version" from dt
9b6fe36 tegra: add path to allow include
60d96e4 platform: tegra: nvadsp: nvadsp_os_get_version fix
79622a2 drivers: tegra: ape firmware version
27fb719 platform: tegra: iommu: enable linear map setup
b77bade platform: nvadsp: fix adsp clock handle
110b085 platform: tegra: Remove code for shared pad control
b770e0e bwmgr: pmqos: fix sparse warning
dd0400a platform: tegra: remove unused tegra_map_common_io
b343db9 platform: tegra: remove Tegra core voltage cap support
df9df6b platform/tegra: remove PMC wakeup support codes
c669b9a platform/tegra: pmc-iopower: switch to upstream PMC driver
688b6a6 platform/tegra: bootrom-pmc: switch to upstream PMC driver
d7d34e6 platform/tegra: remove reboot.c
60b4a47 soc/tegra: pmc: switch to upstream PMC driver
2c9409f bootprof: Separate discontinuous regions
e0cb4d2 arm64: tegra186: remove unused register nodes
12c2ba4 fiq reverts
65d8ccb platform: nvadsp: fix clk checks
b2eb017 platform: tegra: remove support for auto power detection
c403625 tegra: powergate: cleanup code for unsupported platform
589fecb platform: tegra: Removing unsupported platform sources
5f578a8 irqchip: gic: use CPU_PM only when registering
d45c1ea platform: tegra: remove nvdump support for t12x/t13x
9b0b6de Revert "Revert "android: fiq_debugger: FIQ glue for ARM64 CPUs""
d5351a1 Revert "Revert "drivers: platform: enumerate fiq_debugger device""
27af58f Revert "Revert "platform: tegra: clean up fiq_enable() implementation""
688e514 platform: tegra: Remove support for Tegra clock framework
c15896d tegra: denver-knobs: Remove nvmstat_pg stats
019f065 platform: tegra: Remove support for TEGRA_13x_SOC
207db5f drivers: platform: iommu: add linear mapping on lut
1bc0602 denver: hardwood: use device tree to find IRQ number
b11f182 isomgr: Apply ISO client's BW limit
132843c platform: tegra: Remove the API tegra_gpio_to_wake()
8a2892d platform: tegra: deprecate APIs to change waketable runtime
e2f5924 prod: Add support for masked write in partially prod config
e94ac08 platform: tegra: powergate: Remove support for TEGRA_12x_SOC
6b4c4cb platform: tegra: mc: Remove support for TEGRA_12x_SOC
39977fb platform: tegra: Remove drivers for TEGRA_12x_SOC
e17670c drivers: platform: fix denver_mca driver
61c6f5e tegra: powergate: add clocks for XUSBB
bb10919 tegra: powergate: cleanup clock and reset handling
73b944e tegra: powergate: correct xusbb partition reset ID
b3dc4f4 iommu: arm-smmu: add linear mapping support
6c9bcbb platform: tegra: support disabling CONFIG_PM
98d04a5 platform: tegra: remove legacy speedo files
c6b9acf platform: tegra: APIs to set prod based on name/index/offset/mask
a04d242 platform: tegra: mc: fix build error
1d8c939 platform: tegra: Remove miscellaneous platform specific drivers
daab9eb tegra: powergate: shorten some con ids
229a25f platform: tegra: remove tegra common support
3e71442 bwmgr: Remove checks to limit emc_freq
9f3f176 tegra: powergate: use new reset API and use ioremap
11cd9c8 platform: tegra: Disable T210 TCF cpufreq driver
224ecab platform: tegra: Remove the common board support
1813dd1 ivc: fix missing peer notification
a56ac67 ivc: fix incorrect counter reset
844c7a1 ivc: Remove nframes power of two check
5a3ec3a bwmgr: Add more information to clients info sysfs
522777c platform: tegra: remove raydium touch support
251660a platform: tegra: remove unneccessary panel file
0915b9a bwmgr: Add API to get core emc rate
c66f6bc platform: tegra: Add pmqos emc floor handling
a7b51df Add CONFIG_TEGRA_BOND_OUT and disable by default
29cd4ee Stubbed out tegra_periph_reset
3c07fd4 iommu: smmu: Changed the chip ids for 4.4 naming
3d780a1 platform: nvadsp: fix MAXCLKLATENCY offset
e470cdd platform: tegra: bwmgr: add a disable knob
1d8e851 tegra: denver-knobs: Use correct CPU id for bgallowed
db9711f bwmgr: Add errno to debug print
96ed52e drivers: usb: pmc: rebase pmc driver for kernel-4.4
6510703 tegra: denver-knobs: add tracer_control support
3a285a9 tegra: actmon: missing sysfs_attr_init()
0c83659 tegra: actmon: add sysfs node for emc load
2477286 platform: tegra: pmc: add usb2 sleepwalk APIs
8bcf839 pinctrl: add Tegra21x PADCTL/UPHY driver
acaa486 platform: nvadsp: make version func static
6028232 Revert "platform: tegra: clean up fiq_enable() implementation"
cc80660 Revert "drivers: platform: enumerate fiq_debugger device"
ab2cc4c Revert "android: fiq_debugger: FIQ glue for ARM64 CPUs"
38ff9fd drivers: platform: enumerate fiq_debugger device
1dd509c platform: tegra: clean up fiq_enable() implementation
0a87d11 android: fiq_debugger: FIQ glue for ARM64 CPUs
74ec787 platform: nvadsp: add adsp os version
9bd6a7f platform: tegra: Remove use of is_partition_clk_disabled
a56b821 drivers: class 'tegra-firmwares'
c859a13 Kconfig: Rename included Kconfigs
804f706 platform: tegra: add kfuse sensing for hdcp 1.x
9b3510f drivers: platform: tegra: only compile tegra_irq_to_wake for Tegra186
fb5394c drivers: platform: tegra: switch powergating driver to CCF
66d0faf tegra: mc: declare tegra_get_chip_id and use in mc
eb90d56 drivers: platform: Move DPD IO control into pmc driver
3816cef arm64: enable CCF
a196e10 soc/tegra: Add TEGRA148 and TEGRA186 chip id
8354256 drivers: platform: tegra: switch powergate driver to tegra_get_chip_id()
473ce73 platform: tegra: mc: adapt la driver to upstream tegra_get_chip_id()
0f2668b Kconfig: replace source with trysource for external projects
266255a platform: tegra: Add support to find prod setting
ac65ac1 platfor: tegra: prod: Do not use kmemleak_not_leak() for managed allocation
96358ea platform: tegra: prod: use devm_ for allocation
53933ed platform: tegra: Add sysfs entry for suspend/resume time
bfef5bc drivers: platform: tegra: add tegra_wake_to_gpio() interface
4409ca6 tegra: central_actmon: fix DEBUG_FS=n build
80aa543 platform:tegra:bwmgr: fix build issue.
f97d139 tegra: denver-knobs: fix build issue.
bcfc89c platform: tegra: prod: Add managed version of tegra_prod_get_from_node
85288f1 platform: tegra: prod: Add support for nested prod nodes.
0be37d5 platform: tegra: prod: Get rid of tegra_prod_release()
753f71d platform: tegra: prod: Remove unused APIs from public header
5b92965 platform: tegra: pmc: Use devm for tegra_prod allocation
21af9cb platform: tegra: prod: Add APIs to managed allocation of prod_list
2d9312b platform: tegra: move definition of tegra_prod_list structure to private
0d1efe1 platform: tegra: prod: Use for_each_available_child_of_node()
3014a93 tegra:nvadsp:fix issue with CONFIG_DEBUG_FS=n
edd37fd platform: tegra: prod: Use proper variable name
168ec7b platform: tegra: prod: Fix parsing of prod setting for package/board
bc8cd66 platform: tegra: prod: Make allocated memory as kmemleak_not_leak()
380f89f PM/Domains: Remove use of 'need_save' and 'need_restore'
fdf13ea platform: nvadsp: change perms of debugfs files
bc34a73 nvadsp: console: keep track of app context
62d0040 nvadsp: console: fix app name handling
f113a66 platform: nvadsp: export adsp dump sys
e0e907b platform: tegra: nvadsp: fix setting boot freqs
c7fb6ee Revert "drivers: platform: tegra: add proper config check"
c5b1e8b platform:tegra:bwmgr: Fix bwmgr floor aggregation
584b06e platform: tegra: bwmgr: Add Security Engine Client
f63d36d tegra: gpo: move gpio-tegra
8171ecb security: Use a common secure monitor interface
1359955 platform: nvadsp: fix unused function build issue
aa55e67 platform: tegra: Remove unused functions
6f2d8d8 platform: tegra: Change value of need_save to 'true'
0c28fab Merge "Merge agic changes" into dev-kernel
0174199 platform: nvadsp: Correct AGIC irqs state dumps
0d7fec4 irqchip: Move AGIC Kconfig from nvadsp
b7ce47f kernel: change kernel path
7627eeb platform: tegra: mc: fix coverity defects
d4afd62 platform: tegra: remove platform emc driver for Tegra210
93e504f platform: tegra: mc: include module.h
4a3d8fa platform: tegra: add thermal throttling config
b99e994 platform: tegra: move tegra_fiq_debugger from mach-tegra
b89bbf4b tegra: t21x: restore irqs-t21x.h file
3ec5fa9 arm64: Copy over more T210 files
0c3285d platform: tegra: mc: enable latency allowance code compile
80b090f platform: tegra: mc: add T18x PTSA vars
345b7ee tegra: mc: add set LA funcion pointers
26c3314 platform: tegra: Add support for mask with 1s
62e11eb platform: tegra: mc: Don't compile latency_allowance for k4.4
dc9cafc platform: tegra: Add protection to code
55dabeb drivers: platform: tegra: add proper config check
99a65ef platform: tegra: tegra_usb_pmc depends on T210
eae2a6d tegra: denver-knobs: fix seq_printf return value
74467a6 TEMP: drivers: Kconfig: Use source instead of trysource
000acb1 platform: tegra: add missing headers and build fixes
e228595 kconfig: add trysource to kernel-t18x
94daaaa drivers: platform: tegra: Initialize drivers/platform/tegra inside kernel-next
c5c90b8 drivers: platform: tegra: Add miscellaneous platform specific drivers
3faa2fe drivers: platform: tegra: PTM driver for t12x and t21x
8953022 drivers: platform: tegra: Add kfuse driver
19a844c drivers: platform: tegra: Tegra USB Padctrl driver
4596579 drivers: platform: tegra: Add nvdumper source for platforms
a03e4b0 drivers: platform: tegra: Add wakeup related source
363b7ee drivers: platform: tegra: central_actmon: Add common and support for T21x
9fe72d5 drivers: platform: tegra: mc: Add platform specific MC source
6c2b078 drivers: platform: tegra: Adding denver specific drivers
976c8b9 drivers: platform: tegra: Add bootloader drivers
a97be5b drivers: platform: tegra: nvadsp: Add platform specific nvadsp drivers
b29af75 drivers: platform: tegra: Add powergating drivers
899dddd platform: tegra: Add Tegra Clocks drivers for various platforms
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
Change-Id: Ic232ac71a09fe5176247692630db5bc6107573fa
Signed-off-by: Nitin Kumbhar <nkumbhar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1537316
Reviewed-by: svccoveritychecker <svccoveritychecker@nvidia.com>
GVS: Gerrit_Virtual_Submit
961 lines
25 KiB
C
961 lines
25 KiB
C
/*
|
|
* nvadsp_app.c
|
|
*
|
|
* ADSP OS App management
|
|
*
|
|
* Copyright (C) 2014-2015 NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/tegra_nvadsp.h>
|
|
#include <linux/elf.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/kernel.h>
|
|
|
|
#include "os.h"
|
|
#include "dram_app_mem_manager.h"
|
|
#include "adsp_shared_struct.h"
|
|
|
|
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
|
|
# define debug_align(X) ALIGN(X, PAGE_SIZE)
|
|
#else
|
|
# define debug_align(X) (X)
|
|
#endif
|
|
|
|
|
|
#ifndef ARCH_SHF_SMALL
|
|
#define ARCH_SHF_SMALL 0
|
|
#endif
|
|
|
|
#define BITS_PER_INT 32
|
|
#define INIT_OFFSET_MASK (1U < (BITS_PER_INT-1))
|
|
|
|
|
|
#define HWCAP_SWP (1 << 0)
|
|
#define HWCAP_HALF (1 << 1)
|
|
#define HWCAP_THUMB (1 << 2)
|
|
#define HWCAP_26BIT (1 << 3) /* Play it safe */
|
|
#define HWCAP_FAST_MULT (1 << 4)
|
|
#define HWCAP_FPA (1 << 5)
|
|
#define HWCAP_VFP (1 << 6)
|
|
#define HWCAP_EDSP (1 << 7)
|
|
#define HWCAP_JAVA (1 << 8)
|
|
#define HWCAP_IWMMXT (1 << 9)
|
|
#define HWCAP_CRUNCH (1 << 10)
|
|
#define HWCAP_THUMBEE (1 << 11)
|
|
#define HWCAP_NEON (1 << 12)
|
|
#define HWCAP_VFPv3 (1 << 13)
|
|
#define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */
|
|
#define HWCAP_TLS (1 << 15)
|
|
#define HWCAP_VFPv4 (1 << 16)
|
|
#define HWCAP_IDIVA (1 << 17)
|
|
#define HWCAP_IDIVT (1 << 18)
|
|
#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
|
|
#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
|
|
|
|
#define HWCAP_LPAE (1 << 20)
|
|
#define HWCAP_EVTSTRM_32 (1 << 21)
|
|
|
|
|
|
#define EF_ARM_EABI_MASK 0xff000000
|
|
#define EF_ARM_EABI_UNKNOWN 0x00000000
|
|
#define EF_ARM_EABI_VER1 0x01000000
|
|
#define EF_ARM_EABI_VER2 0x02000000
|
|
#define EF_ARM_EABI_VER3 0x03000000
|
|
#define EF_ARM_EABI_VER4 0x04000000
|
|
#define EF_ARM_EABI_VER5 0x05000000
|
|
|
|
#define EF_ARM_BE8 0x00800000 /* ABI 4,5 */
|
|
#define EF_ARM_LE8 0x00400000 /* ABI 4,5 */
|
|
#define EF_ARM_MAVERICK_FLOAT 0x00000800 /* ABI 0 */
|
|
#define EF_ARM_VFP_FLOAT 0x00000400 /* ABI 0 */
|
|
#define EF_ARM_SOFT_FLOAT 0x00000200 /* ABI 0 */
|
|
#define EF_ARM_OLD_ABI 0x00000100 /* ABI 0 */
|
|
#define EF_ARM_NEW_ABI 0x00000080 /* ABI 0 */
|
|
#define EF_ARM_ALIGN8 0x00000040 /* ABI 0 */
|
|
#define EF_ARM_PIC 0x00000020 /* ABI 0 */
|
|
#define EF_ARM_MAPSYMSFIRST 0x00000010 /* ABI 2 */
|
|
#define EF_ARM_APCS_FLOAT 0x00000010 /* ABI 0, floats in fp regs */
|
|
#define EF_ARM_DYNSYMSUSESEGIDX 0x00000008 /* ABI 2 */
|
|
#define EF_ARM_APCS_26 0x00000008 /* ABI 0 */
|
|
#define EF_ARM_SYMSARESORTED 0x00000004 /* ABI 1,2 */
|
|
#define EF_ARM_INTERWORK 0x00000004 /* ABI 0 */
|
|
#define EF_ARM_HASENTRY 0x00000002 /* All */
|
|
#define EF_ARM_RELEXEC 0x00000001 /* All */
|
|
|
|
|
|
#define R_ARM_NONE 0
|
|
#define R_ARM_PC24 1
|
|
#define R_ARM_ABS32 2
|
|
#define R_ARM_CALL 28
|
|
#define R_ARM_JUMP24 29
|
|
#define R_ARM_TARGET1 38
|
|
#define R_ARM_V4BX 40
|
|
#define R_ARM_PREL31 42
|
|
#define R_ARM_MOVW_ABS_NC 43
|
|
#define R_ARM_MOVT_ABS 44
|
|
|
|
#define R_ARM_THM_CALL 10
|
|
#define R_ARM_THM_JUMP24 30
|
|
#define R_ARM_THM_MOVW_ABS_NC 47
|
|
#define R_ARM_THM_MOVT_ABS 48
|
|
|
|
|
|
struct load_info {
|
|
const char *name;
|
|
struct elf32_hdr *hdr;
|
|
unsigned long len;
|
|
struct elf32_shdr *sechdrs;
|
|
char *secstrings, *strtab;
|
|
unsigned long symoffs, stroffs;
|
|
unsigned int num_debug;
|
|
bool sig_ok;
|
|
struct device *dev;
|
|
struct {
|
|
unsigned int sym, str, mod, vers, info, pcpu;
|
|
} index;
|
|
};
|
|
|
|
static int
|
|
apply_relocate(const struct load_info *info, Elf32_Shdr *sechdrs,
|
|
const char *strtab, unsigned int symindex,
|
|
unsigned int relindex, struct adsp_module *module)
|
|
{
|
|
Elf32_Shdr *symsec = sechdrs + symindex;
|
|
Elf32_Shdr *relsec = sechdrs + relindex;
|
|
Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
|
|
Elf32_Rel *rel = (void *)info->hdr + relsec->sh_offset;
|
|
struct device *dev = info->dev;
|
|
unsigned int i;
|
|
|
|
dev_dbg(dev, "the relative section is %s dst %s sym %s\n",
|
|
info->secstrings + relsec->sh_name,
|
|
info->secstrings + dstsec->sh_name,
|
|
info->secstrings + symsec->sh_name);
|
|
|
|
for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) {
|
|
void *loc;
|
|
Elf32_Sym *sym;
|
|
const char *symname;
|
|
s32 offset;
|
|
u32 upper, lower, sign, j1, j2;
|
|
uint32_t adsp_loc;
|
|
bool switch_mode = false;
|
|
int h_bit = 0;
|
|
|
|
offset = ELF32_R_SYM(rel->r_info);
|
|
if (offset < 0 || (offset >
|
|
(symsec->sh_size / sizeof(Elf32_Sym)))) {
|
|
dev_err(dev, "%s: section %u reloc %u: bad relocation sym offset\n",
|
|
module->name, relindex, i);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
sym = ((Elf32_Sym *)(module->module_ptr
|
|
+ symsec->sh_addr)) + offset;
|
|
symname = info->strtab + sym->st_name;
|
|
|
|
dev_dbg(dev, "%s\n", symname);
|
|
|
|
if (rel->r_offset < 0 ||
|
|
rel->r_offset > dstsec->sh_size - sizeof(u32)) {
|
|
dev_err(dev,
|
|
"%s: section %u reloc %u sym '%s': out of bounds relocation, offset %d size %u\n",
|
|
module->name, relindex, i, symname,
|
|
rel->r_offset, dstsec->sh_size);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
loc = module->module_ptr + dstsec->sh_addr + rel->r_offset;
|
|
adsp_loc = module->adsp_module_ptr +
|
|
dstsec->sh_addr + rel->r_offset;
|
|
dev_dbg(dev, "%p 0x%x\n", loc, adsp_loc);
|
|
|
|
if (ELF_ST_BIND(sym->st_info) == STB_WEAK
|
|
&& sym->st_shndx == SHN_UNDEF) {
|
|
dev_dbg(dev, "STB_WEAK %s\n", symname);
|
|
continue;
|
|
}
|
|
|
|
switch (ELF32_R_TYPE(rel->r_info)) {
|
|
case R_ARM_NONE:
|
|
dev_dbg(dev, "R_ARM_NONE\n");
|
|
/* ignore */
|
|
break;
|
|
|
|
case R_ARM_ABS32:
|
|
case R_ARM_TARGET1:
|
|
dev_dbg(dev, "R_ARM_ABS32\n");
|
|
*(u32 *)loc += sym->st_value;
|
|
dev_dbg(dev, "addrs: 0x%x %p values: 0x%x 0x%x\n",
|
|
adsp_loc, loc, sym->st_value,
|
|
*(u32 *)loc);
|
|
break;
|
|
|
|
case R_ARM_PC24:
|
|
case R_ARM_CALL:
|
|
case R_ARM_JUMP24:
|
|
dev_dbg(dev, "R_ARM_CALL R_ARM_JUMP24\n");
|
|
offset = (*(u32 *)loc & 0x00ffffff) << 2;
|
|
if (offset & 0x02000000)
|
|
offset -= 0x04000000;
|
|
|
|
offset += sym->st_value - adsp_loc;
|
|
if ((ELF32_ST_TYPE(sym->st_info) == STT_FUNC)
|
|
&& (offset & 3)) {
|
|
dev_dbg(dev, "switching the mode from ARM to THUMB\n");
|
|
switch_mode = true;
|
|
h_bit = (offset & 2);
|
|
dev_dbg(dev,
|
|
"%s offset 0x%x hbit %d",
|
|
symname, offset, h_bit);
|
|
}
|
|
|
|
if (offset <= (s32)0xfe000000 ||
|
|
offset >= (s32)0x02000000) {
|
|
dev_err(dev,
|
|
"%s: section %u reloc %u sym '%s': relocation %u out of range (%p -> %#x)\n",
|
|
module->name, relindex, i, symname,
|
|
ELF32_R_TYPE(rel->r_info), loc,
|
|
sym->st_value);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
offset >>= 2;
|
|
|
|
*(u32 *)loc &= 0xff000000;
|
|
*(u32 *)loc |= offset & 0x00ffffff;
|
|
if (switch_mode) {
|
|
*(u32 *)loc &= ~(0xff000000);
|
|
if (h_bit)
|
|
*(u32 *)loc |= 0xfb000000;
|
|
else
|
|
*(u32 *)loc |= 0xfa000000;
|
|
}
|
|
|
|
dev_dbg(dev,
|
|
"%s address 0x%x instruction 0x%x\n",
|
|
symname, adsp_loc, *(u32 *)loc);
|
|
break;
|
|
|
|
case R_ARM_V4BX:
|
|
dev_dbg(dev, "R_ARM_V4BX\n");
|
|
/* Preserve Rm and the condition code. Alter
|
|
* other bits to re-code instruction as
|
|
* MOV PC,Rm.
|
|
*/
|
|
*(u32 *)loc &= 0xf000000f;
|
|
*(u32 *)loc |= 0x01a0f000;
|
|
break;
|
|
|
|
case R_ARM_PREL31:
|
|
dev_dbg(dev, "R_ARM_PREL31\n");
|
|
offset = *(u32 *)loc + sym->st_value - adsp_loc;
|
|
*(u32 *)loc = offset & 0x7fffffff;
|
|
break;
|
|
|
|
case R_ARM_MOVW_ABS_NC:
|
|
case R_ARM_MOVT_ABS:
|
|
dev_dbg(dev, "R_ARM_MOVT_ABS\n");
|
|
offset = *(u32 *)loc;
|
|
offset = ((offset & 0xf0000) >> 4) | (offset & 0xfff);
|
|
offset = (offset ^ 0x8000) - 0x8000;
|
|
|
|
offset += sym->st_value;
|
|
if (ELF32_R_TYPE(rel->r_info) == R_ARM_MOVT_ABS)
|
|
offset >>= 16;
|
|
|
|
*(u32 *)loc &= 0xfff0f000;
|
|
*(u32 *)loc |= ((offset & 0xf000) << 4) |
|
|
(offset & 0x0fff);
|
|
break;
|
|
|
|
case R_ARM_THM_CALL:
|
|
case R_ARM_THM_JUMP24:
|
|
dev_dbg(dev, "R_ARM_THM_CALL R_ARM_THM_JUMP24\n");
|
|
upper = *(u16 *)loc;
|
|
lower = *(u16 *)(loc + 2);
|
|
|
|
/*
|
|
* 25 bit signed address range (Thumb-2 BL and B.W
|
|
* instructions):
|
|
* S:I1:I2:imm10:imm11:0
|
|
* where:
|
|
* S = upper[10] = offset[24]
|
|
* I1 = ~(J1 ^ S) = offset[23]
|
|
* I2 = ~(J2 ^ S) = offset[22]
|
|
* imm10 = upper[9:0] = offset[21:12]
|
|
* imm11 = lower[10:0] = offset[11:1]
|
|
* J1 = lower[13]
|
|
* J2 = lower[11]
|
|
*/
|
|
sign = (upper >> 10) & 1;
|
|
j1 = (lower >> 13) & 1;
|
|
j2 = (lower >> 11) & 1;
|
|
offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) |
|
|
((~(j2 ^ sign) & 1) << 22) |
|
|
((upper & 0x03ff) << 12) |
|
|
((lower & 0x07ff) << 1);
|
|
if (offset & 0x01000000)
|
|
offset -= 0x02000000;
|
|
offset += sym->st_value - adsp_loc;
|
|
|
|
/*
|
|
* For function symbols, only Thumb addresses are
|
|
* allowed (no interworking).
|
|
*
|
|
* For non-function symbols, the destination
|
|
* has no specific ARM/Thumb disposition, so
|
|
* the branch is resolved under the assumption
|
|
* that interworking is not required.
|
|
*/
|
|
if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC &&
|
|
!(offset & 1)) {
|
|
dev_dbg(dev,
|
|
"switching the mode from THUMB to ARM\n");
|
|
switch_mode = true;
|
|
offset = ALIGN(offset, 4);
|
|
}
|
|
|
|
if (offset <= (s32)0xff000000 ||
|
|
offset >= (s32)0x01000000) {
|
|
dev_err(dev,
|
|
"%s: section %u reloc %u sym '%s': relocation %u out of range (%p -> %#x)\n",
|
|
module->name, relindex, i, symname,
|
|
ELF32_R_TYPE(rel->r_info), loc,
|
|
sym->st_value);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
sign = (offset >> 24) & 1;
|
|
j1 = sign ^ (~(offset >> 23) & 1);
|
|
j2 = sign ^ (~(offset >> 22) & 1);
|
|
*(u16 *)loc = (u16)((upper & 0xf800) | (sign << 10) |
|
|
((offset >> 12) & 0x03ff));
|
|
*(u16 *)(loc + 2) = (u16)((lower & 0xd000) |
|
|
(j1 << 13) | (j2 << 11) |
|
|
((offset >> 1) & 0x07ff));
|
|
|
|
if (switch_mode) {
|
|
lower = *(u16 *)(loc + 2);
|
|
lower &= (~(1 << 12));
|
|
*(u16 *)(loc + 2) = lower;
|
|
}
|
|
|
|
dev_dbg(dev,
|
|
"%s address 0x%x upper instruction 0x%x\n",
|
|
symname, adsp_loc, *(u16 *)loc);
|
|
dev_dbg(dev,
|
|
"%s address 0x%x lower instruction 0x%x\n",
|
|
symname, adsp_loc, *(u16 *)(loc + 2));
|
|
break;
|
|
|
|
case R_ARM_THM_MOVW_ABS_NC:
|
|
case R_ARM_THM_MOVT_ABS:
|
|
dev_dbg(dev, "in R_ARM_THM_MOVT_ABS\n");
|
|
upper = *(u16 *)loc;
|
|
lower = *(u16 *)(loc + 2);
|
|
|
|
/*
|
|
* MOVT/MOVW instructions encoding in Thumb-2:
|
|
*
|
|
* i = upper[10]
|
|
* imm4 = upper[3:0]
|
|
* imm3 = lower[14:12]
|
|
* imm8 = lower[7:0]
|
|
*
|
|
* imm16 = imm4:i:imm3:imm8
|
|
*/
|
|
offset = ((upper & 0x000f) << 12) |
|
|
((upper & 0x0400) << 1) |
|
|
((lower & 0x7000) >> 4) | (lower & 0x00ff);
|
|
offset = (offset ^ 0x8000) - 0x8000;
|
|
offset += sym->st_value;
|
|
|
|
if (ELF32_R_TYPE(rel->r_info) == R_ARM_THM_MOVT_ABS)
|
|
offset >>= 16;
|
|
|
|
*(u16 *)loc = (u16)((upper & 0xfbf0) |
|
|
((offset & 0xf000) >> 12) |
|
|
((offset & 0x0800) >> 1));
|
|
*(u16 *)(loc + 2) = (u16)((lower & 0x8f00) |
|
|
((offset & 0x0700) << 4) |
|
|
(offset & 0x00ff));
|
|
break;
|
|
|
|
default:
|
|
dev_err(dev, "%s: unknown relocation: %u\n",
|
|
module->name, ELF32_R_TYPE(rel->r_info));
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
apply_relocations(struct adsp_module *mod,
|
|
const struct load_info *info)
|
|
{
|
|
unsigned int i;
|
|
int err = 0;
|
|
|
|
/* Now do relocations. */
|
|
for (i = 1; i < info->hdr->e_shnum; i++) {
|
|
unsigned int infosec = info->sechdrs[i].sh_info;
|
|
|
|
/* Not a valid relocation section? */
|
|
if (infosec >= info->hdr->e_shnum)
|
|
continue;
|
|
|
|
/* Don't bother with non-allocated sections */
|
|
if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
|
|
continue;
|
|
|
|
if (info->sechdrs[i].sh_type == SHT_REL)
|
|
err = apply_relocate(info, info->sechdrs, info->strtab,
|
|
info->index.sym, i, mod);
|
|
else if (info->sechdrs[i].sh_type == SHT_RELA)
|
|
return -EINVAL;
|
|
if (err < 0)
|
|
break;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
simplify_symbols(struct adsp_module *mod,
|
|
const struct load_info *info)
|
|
{
|
|
Elf32_Shdr *symsec = &info->sechdrs[info->index.sym];
|
|
Elf32_Sym *sym = mod->module_ptr + symsec->sh_addr;
|
|
unsigned int secbase;
|
|
unsigned int i;
|
|
int ret = 0;
|
|
struct global_sym_info *sym_info;
|
|
struct device *dev = info->dev;
|
|
|
|
for (i = 1; i < symsec->sh_size / sizeof(Elf32_Sym); i++) {
|
|
const char *name = info->strtab + sym[i].st_name;
|
|
dev_dbg(dev, "%s\n", name);
|
|
switch (sym[i].st_shndx) {
|
|
case SHN_COMMON:
|
|
/* We compiled with -fno-common. These are not
|
|
supposed to happen. */
|
|
dev_err(dev, "Common symbol: '%s'\n", name);
|
|
dev_err(dev,
|
|
"please compile module %s with -fno-common\n",
|
|
mod->name);
|
|
ret = -ENOEXEC;
|
|
goto end;
|
|
|
|
case SHN_ABS:
|
|
/* Don't need to do anything */
|
|
dev_dbg(dev, "Absolute symbol: 0x%08lx\n",
|
|
(long)sym[i].st_value);
|
|
break;
|
|
|
|
case SHN_UNDEF:
|
|
sym_info = find_global_symbol(name);
|
|
|
|
/* Ok if resolved. */
|
|
if (sym_info) {
|
|
dev_dbg(dev, "SHN_UNDEF sym '%s':0x%x\n",
|
|
name, sym_info->addr);
|
|
sym[i].st_value = sym_info->addr;
|
|
sym[i].st_info = sym_info->info;
|
|
break;
|
|
}
|
|
|
|
if (ELF_ST_BIND(sym[i].st_info) == STB_WEAK) {
|
|
dev_dbg(dev, "WEAK SYM %s not resolved\n",
|
|
name);
|
|
break;
|
|
}
|
|
|
|
dev_err(dev, "No symbol '%s' found\n", name);
|
|
ret = -ENOEXEC;
|
|
goto end;
|
|
|
|
default:
|
|
/* Divert to percpu allocation if a percpu var. */
|
|
dev_dbg(dev, "default\n");
|
|
secbase = info->sechdrs[sym[i].st_shndx].sh_addr;
|
|
sym[i].st_value += secbase + mod->adsp_module_ptr;
|
|
dev_dbg(dev, "symbol %s is 0x%x\n",
|
|
name, sym[i].st_value);
|
|
break;
|
|
}
|
|
}
|
|
end:
|
|
return ret;
|
|
}
|
|
|
|
static int move_module(struct adsp_module *mod, struct load_info *info)
|
|
{
|
|
struct device *dev = info->dev;
|
|
int i;
|
|
|
|
mod->handle = dram_app_mem_request(info->name, mod->size);
|
|
if (!mod->handle) {
|
|
dev_err(dev, "cannot allocate memory for app %s\n", info->name);
|
|
return -ENOMEM;
|
|
}
|
|
mod->adsp_module_ptr = dram_app_mem_get_address(mod->handle);
|
|
mod->module_ptr = nvadsp_da_to_va_mappings(mod->adsp_module_ptr,
|
|
mod->size);
|
|
dev_info(dev, "module %s Load address %p 0x%x\n", info->name,
|
|
mod->module_ptr, mod->adsp_module_ptr);
|
|
/* Transfer each section which specifies SHF_ALLOC */
|
|
dev_dbg(dev, "final section addresses:\n");
|
|
for (i = 0; i < info->hdr->e_shnum; i++) {
|
|
void *dest;
|
|
struct elf32_shdr *shdr = &info->sechdrs[i];
|
|
|
|
if (!(shdr->sh_flags & SHF_ALLOC))
|
|
continue;
|
|
|
|
if (shdr->sh_entsize & INIT_OFFSET_MASK) {
|
|
dev_dbg(dev, "%s %d\n",
|
|
info->secstrings + shdr->sh_name,
|
|
shdr->sh_entsize);
|
|
dest = mod->module_ptr
|
|
+ (shdr->sh_entsize & ~INIT_OFFSET_MASK);
|
|
} else {
|
|
dev_dbg(dev, "%s %d\n",
|
|
info->secstrings + shdr->sh_name,
|
|
shdr->sh_entsize);
|
|
dest = mod->module_ptr + shdr->sh_entsize;
|
|
}
|
|
|
|
if (shdr->sh_type != SHT_NOBITS)
|
|
memcpy(dest,
|
|
(void *)info->hdr + shdr->sh_offset,
|
|
shdr->sh_size);
|
|
/* Update sh_addr to point to copy in image. */
|
|
shdr->sh_addr = (uint32_t)(dest - mod->module_ptr);
|
|
dev_dbg(dev, "name %s 0x%x %p 0x%x 0x%x\n",
|
|
info->secstrings + shdr->sh_name, shdr->sh_addr,
|
|
dest, shdr->sh_addr + mod->adsp_module_ptr,
|
|
shdr->sh_size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_offset(struct adsp_module *mod, size_t *size,
|
|
struct elf32_shdr *sechdr, unsigned int section)
|
|
{
|
|
int ret;
|
|
|
|
ret = ALIGN(*size, sechdr->sh_addralign ?: 1);
|
|
*size = ret + sechdr->sh_size;
|
|
return ret;
|
|
}
|
|
|
|
static bool
|
|
is_core_symbol(const struct elf32_sym *src,
|
|
const struct elf32_shdr *sechdrs, unsigned int shnum)
|
|
{
|
|
const struct elf32_shdr *sec;
|
|
|
|
if (src->st_shndx == SHN_UNDEF
|
|
|| src->st_shndx >= shnum
|
|
|| !src->st_name)
|
|
return false;
|
|
|
|
sec = sechdrs + src->st_shndx;
|
|
if (!(sec->sh_flags & SHF_ALLOC)
|
|
#ifndef CONFIG_KALLSYMS_ALL
|
|
|| !(sec->sh_flags & SHF_EXECINSTR)
|
|
#endif
|
|
|| (sec->sh_entsize & INIT_OFFSET_MASK))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void layout_sections(struct adsp_module *mod, struct load_info *info)
|
|
{
|
|
static unsigned long const masks[][2] = {
|
|
/* NOTE: all executable code must be the first section
|
|
* in this array; otherwise modify the text_size
|
|
* finder in the two loops below */
|
|
{ SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },
|
|
{ SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },
|
|
{ SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },
|
|
{ ARCH_SHF_SMALL | SHF_ALLOC, 0 }
|
|
};
|
|
unsigned int m, i;
|
|
struct device *dev = info->dev;
|
|
|
|
for (i = 0; i < info->hdr->e_shnum; i++)
|
|
info->sechdrs[i].sh_entsize = ~0U;
|
|
|
|
dev_dbg(dev, "Core section allocation order:\n");
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
for (i = 0; i < info->hdr->e_shnum; ++i) {
|
|
struct elf32_shdr *s = &info->sechdrs[i];
|
|
const char *sname = info->secstrings + s->sh_name;
|
|
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
|| (s->sh_flags & masks[m][1])
|
|
|| s->sh_entsize != ~0U
|
|
|| strstarts(sname, ".init"))
|
|
continue;
|
|
|
|
s->sh_entsize = get_offset(mod, &mod->size, s, i);
|
|
dev_dbg(dev, "\t%s %d\n", sname, s->sh_entsize);
|
|
}
|
|
}
|
|
|
|
dev_dbg(dev, "Init section allocation order:\n");
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
for (i = 0; i < info->hdr->e_shnum; ++i) {
|
|
struct elf32_shdr *s = &info->sechdrs[i];
|
|
const char *sname = info->secstrings + s->sh_name;
|
|
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
|| (s->sh_flags & masks[m][1])
|
|
|| s->sh_entsize != ~0U
|
|
|| !strstarts(sname, ".init"))
|
|
continue;
|
|
s->sh_entsize = (get_offset(mod, &mod->size, s, i)
|
|
| INIT_OFFSET_MASK);
|
|
dev_dbg(dev, "\t%s %d\n", sname, s->sh_entsize);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int rewrite_section_headers(struct load_info *info)
|
|
{
|
|
unsigned int i;
|
|
struct device *dev = info->dev;
|
|
|
|
/* This should always be true, but let's be sure. */
|
|
info->sechdrs[0].sh_addr = 0;
|
|
|
|
for (i = 1; i < info->hdr->e_shnum; i++) {
|
|
struct elf32_shdr *shdr = &info->sechdrs[i];
|
|
if (shdr->sh_type != SHT_NOBITS
|
|
&& info->len < shdr->sh_offset + shdr->sh_size) {
|
|
dev_err(dev, "Module len %lu truncated\n", info->len);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
/* Mark all sections sh_addr with their address in the
|
|
temporary image. */
|
|
shdr->sh_addr = shdr->sh_offset;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct adsp_module *setup_load_info(struct load_info *info)
|
|
{
|
|
unsigned int i;
|
|
int err;
|
|
struct adsp_module *mod;
|
|
struct device *dev = info->dev;
|
|
|
|
/* Set up the convenience variables */
|
|
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
|
|
info->secstrings = (void *)info->hdr
|
|
+ info->sechdrs[info->hdr->e_shstrndx].sh_offset;
|
|
|
|
err = rewrite_section_headers(info);
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
|
|
/* Find internal symbols and strings. */
|
|
for (i = 1; i < info->hdr->e_shnum; i++) {
|
|
if (info->sechdrs[i].sh_type == SHT_SYMTAB) {
|
|
info->index.sym = i;
|
|
info->index.str = info->sechdrs[i].sh_link;
|
|
info->strtab = (char *)info->hdr
|
|
+ info->sechdrs[info->index.str].sh_offset;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* This is temporary: point mod into copy of data. */
|
|
mod = kzalloc(sizeof(struct adsp_module), GFP_KERNEL);
|
|
if (!mod) {
|
|
dev_err(dev, "Unable to create module\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
if (info->index.sym == 0) {
|
|
dev_warn(dev, "%s: module has no symbols (stripped?)\n",
|
|
mod->name);
|
|
kfree(mod);
|
|
return ERR_PTR(-ENOEXEC);
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
|
|
static void layout_symtab(struct adsp_module *mod, struct load_info *info)
|
|
{
|
|
struct elf32_shdr *symsect = info->sechdrs + info->index.sym;
|
|
struct elf32_shdr *strsect = info->sechdrs + info->index.str;
|
|
const struct elf32_sym *src;
|
|
unsigned int i, nsrc, ndst, strtab_size = 0;
|
|
struct device *dev = info->dev;
|
|
|
|
/* Put symbol section at end of init part of module. */
|
|
symsect->sh_flags |= SHF_ALLOC;
|
|
symsect->sh_entsize = get_offset(mod, &mod->size, symsect,
|
|
info->index.sym) | INIT_OFFSET_MASK;
|
|
dev_dbg(dev, "\t%s %d\n", info->secstrings + symsect->sh_name,
|
|
symsect->sh_entsize);
|
|
|
|
src = (void *)info->hdr + symsect->sh_offset;
|
|
nsrc = symsect->sh_size / sizeof(*src);
|
|
|
|
/* Compute total space required for the core symbols' strtab. */
|
|
for (ndst = i = 0; i < nsrc; i++) {
|
|
if (i == 0 ||
|
|
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
|
|
strtab_size += strlen(&info->strtab[src[i].st_name])+1;
|
|
ndst++;
|
|
}
|
|
}
|
|
|
|
/* Append room for core symbols at end of core part. */
|
|
info->symoffs = ALIGN(mod->size, symsect->sh_addralign ?: 1);
|
|
info->stroffs = mod->size = info->symoffs + ndst * sizeof(Elf32_Sym);
|
|
mod->size += strtab_size;
|
|
|
|
/* Put string table section at end of init part of module. */
|
|
strsect->sh_flags |= SHF_ALLOC;
|
|
strsect->sh_entsize = get_offset(mod, &mod->size, strsect,
|
|
info->index.str) | INIT_OFFSET_MASK;
|
|
dev_dbg(dev, "\t%s %d\n",
|
|
info->secstrings + strsect->sh_name,
|
|
symsect->sh_entsize);
|
|
}
|
|
|
|
static struct adsp_module *layout_and_allocate(struct load_info *info)
|
|
{
|
|
/* Module within temporary copy. */
|
|
struct adsp_module *mod;
|
|
int err;
|
|
|
|
mod = setup_load_info(info);
|
|
if (IS_ERR(mod))
|
|
return mod;
|
|
|
|
mod->name = info->name;
|
|
|
|
/* Determine total sizes, and put offsets in sh_entsize. For now
|
|
this is done generically; there doesn't appear to be any
|
|
special cases for the architectures. */
|
|
layout_sections(mod, info);
|
|
layout_symtab(mod, info);
|
|
|
|
/* Allocate and move to the final place */
|
|
err = move_module(mod, info);
|
|
if (err) {
|
|
/* TODO: need to handle error path more genericly */
|
|
kfree(mod);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
static int elf_check_arch_arm32(const struct elf32_hdr *x)
|
|
{
|
|
unsigned int eflags;
|
|
|
|
/* Make sure it's an ARM executable */
|
|
if (x->e_machine != EM_ARM)
|
|
return 0;
|
|
|
|
/* Make sure the entry address is reasonable */
|
|
if (x->e_entry & 1) {
|
|
if (!(elf_hwcap & HWCAP_THUMB))
|
|
return 0;
|
|
} else if (x->e_entry & 3)
|
|
return 0;
|
|
|
|
eflags = x->e_flags;
|
|
if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN) {
|
|
unsigned int flt_fmt;
|
|
|
|
/* APCS26 is only allowed if the CPU supports it */
|
|
if ((eflags & EF_ARM_APCS_26) && !(elf_hwcap & HWCAP_26BIT))
|
|
return 0;
|
|
|
|
flt_fmt = eflags & (EF_ARM_VFP_FLOAT | EF_ARM_SOFT_FLOAT);
|
|
|
|
/* VFP requires the supporting code */
|
|
if (flt_fmt == EF_ARM_VFP_FLOAT && !(elf_hwcap & HWCAP_VFP))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int elf_header_check(struct load_info *info)
|
|
{
|
|
if (info->len < sizeof(*(info->hdr)))
|
|
return -ENOEXEC;
|
|
|
|
if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0
|
|
|| info->hdr->e_type != ET_REL
|
|
|| !elf_check_arch_arm32(info->hdr)
|
|
|| info->hdr->e_shentsize != sizeof(Elf32_Shdr))
|
|
return -ENOEXEC;
|
|
|
|
if (info->hdr->e_shoff >= info->len
|
|
|| (info->hdr->e_shnum * sizeof(Elf32_Shdr) >
|
|
info->len - info->hdr->e_shoff))
|
|
return -ENOEXEC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct adsp_module *load_adsp_static_module(const char *appname,
|
|
struct adsp_shared_app *shared_app, struct device *dev)
|
|
{
|
|
struct adsp_module *mod = NULL;
|
|
|
|
mod = kzalloc(sizeof(struct adsp_module), GFP_KERNEL);
|
|
if (!mod)
|
|
return NULL;
|
|
|
|
memcpy((struct app_mem_size *)&mod->mem_size,
|
|
&shared_app->mem_size, sizeof(shared_app->mem_size));
|
|
|
|
mod->adsp_module_ptr = shared_app->mod_ptr;
|
|
mod->dynamic = false;
|
|
|
|
return mod;
|
|
}
|
|
|
|
struct adsp_module *load_adsp_dynamic_module(const char *appname,
|
|
const char *appfile, struct device *dev)
|
|
{
|
|
struct load_info info = { };
|
|
struct adsp_module *mod = NULL;
|
|
const struct firmware *fw;
|
|
struct elf32_shdr *data_shdr;
|
|
struct elf32_shdr *shared_shdr;
|
|
struct elf32_shdr *shared_wc_shdr;
|
|
struct elf32_shdr *aram_shdr;
|
|
struct elf32_shdr *aram_x_shdr;
|
|
struct app_mem_size *mem_size;
|
|
int ret;
|
|
|
|
ret = request_firmware(&fw, appfile, dev);
|
|
if (ret < 0) {
|
|
dev_err(dev,
|
|
"request firmware for %s(%s) failed with %d\n",
|
|
appname, appfile, ret);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
info.hdr = (struct elf32_hdr *)fw->data;
|
|
info.len = fw->size;
|
|
info.dev = dev;
|
|
info.name = appname;
|
|
|
|
ret = elf_header_check(&info);
|
|
if (ret) {
|
|
dev_err(dev,
|
|
"%s is not an elf file\n", appfile);
|
|
goto error_release_fw;
|
|
}
|
|
|
|
/* Figure out module layout, and allocate all the memory. */
|
|
mod = layout_and_allocate(&info);
|
|
if (IS_ERR(mod))
|
|
goto error_release_fw;
|
|
|
|
/* update adsp specific sections */
|
|
data_shdr = nvadsp_get_section(fw, ".dram_data");
|
|
shared_shdr = nvadsp_get_section(fw, ".dram_shared");
|
|
shared_wc_shdr = nvadsp_get_section(fw, ".dram_shared_wc");
|
|
aram_shdr = nvadsp_get_section(fw, ".aram_data");
|
|
aram_x_shdr = nvadsp_get_section(fw, ".aram_x_data");
|
|
|
|
mem_size = (void *)&mod->mem_size;
|
|
|
|
if (data_shdr) {
|
|
dev_dbg(dev, "mem_size.dram_data %d\n",
|
|
data_shdr->sh_size);
|
|
mem_size->dram = data_shdr->sh_size;
|
|
}
|
|
|
|
if (shared_shdr) {
|
|
dev_dbg(dev, "mem_size.dram_shared %d\n",
|
|
shared_shdr->sh_size);
|
|
mem_size->dram_shared =
|
|
shared_shdr->sh_size;
|
|
}
|
|
|
|
if (shared_wc_shdr) {
|
|
dev_dbg(dev, "shared_wc_shdr->sh_size %d\n",
|
|
shared_wc_shdr->sh_size);
|
|
mem_size->dram_shared_wc =
|
|
shared_wc_shdr->sh_size;
|
|
}
|
|
|
|
if (aram_shdr) {
|
|
dev_dbg(dev, "aram_shdr->sh_size %d\n", aram_shdr->sh_size);
|
|
mem_size->aram = aram_shdr->sh_size;
|
|
}
|
|
|
|
if (aram_x_shdr) {
|
|
dev_dbg(dev,
|
|
"aram_x_shdr->sh_size %d\n", aram_x_shdr->sh_size);
|
|
mem_size->aram_x = aram_x_shdr->sh_size;
|
|
}
|
|
|
|
/* Fix up syms, so that st_value is a pointer to location. */
|
|
ret = simplify_symbols(mod, &info);
|
|
if (ret) {
|
|
dev_err(dev, "Unable to simplify symbols\n");
|
|
goto unload_module;
|
|
}
|
|
|
|
dev_dbg(dev, "applying relocation\n");
|
|
ret = apply_relocations(mod, &info);
|
|
if (ret) {
|
|
dev_err(dev, "relocation failed\n");
|
|
goto unload_module;
|
|
}
|
|
|
|
mod->dynamic = true;
|
|
|
|
error_release_fw:
|
|
release_firmware(fw);
|
|
return IS_ERR_VALUE(ret) ? ERR_PTR(ret) : mod;
|
|
|
|
unload_module:
|
|
unload_adsp_module(mod);
|
|
release_firmware(fw);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
void unload_adsp_module(struct adsp_module *mod)
|
|
{
|
|
dram_app_mem_release(mod->handle);
|
|
kfree(mod);
|
|
}
|