2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-07-15 12:25:27 +04:00
/*
* Remote Processor Framework Elf loader
*
* Copyright ( C ) 2011 Texas Instruments , Inc .
* Copyright ( C ) 2011 Google , Inc .
*
* Ohad Ben - Cohen < ohad @ wizery . com >
* Brian Swetland < swetland @ google . com >
* Mark Grosen < mgrosen @ ti . com >
* Fernando Guzman Lugo < fernando . lugo @ ti . com >
* Suman Anna < s - anna @ ti . com >
* Robert Tivy < rtivy @ ti . com >
* Armando Uribe De Leon < x0095078 @ ti . com >
* Sjur Brændeland < sjur . brandeland @ stericsson . com >
*/
# define pr_fmt(fmt) "%s: " fmt, __func__
# include <linux/module.h>
# include <linux/firmware.h>
# include <linux/remoteproc.h>
# include <linux/elf.h>
# include "remoteproc_internal.h"
2020-03-02 12:39:00 +03:00
# include "remoteproc_elf_helpers.h"
2012-07-15 12:25:27 +04:00
/**
2020-03-02 12:39:00 +03:00
* rproc_elf_sanity_check ( ) - Sanity Check for ELF32 / ELF64 firmware image
2012-07-15 12:25:27 +04:00
* @ rproc : the remote processor handle
* @ fw : the ELF firmware image
*
2020-03-02 12:39:00 +03:00
* Make sure this fw image is sane ( ie a correct ELF32 / ELF64 file ) .
2021-05-19 21:03:04 +03:00
*
* Return : 0 on success and - EINVAL upon any failure
2012-07-15 12:25:27 +04:00
*/
2020-03-02 12:39:00 +03:00
int rproc_elf_sanity_check ( struct rproc * rproc , const struct firmware * fw )
2012-07-15 12:25:27 +04:00
{
const char * name = rproc - > firmware ;
struct device * dev = & rproc - > dev ;
2020-03-02 12:39:00 +03:00
/*
* 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 .
*/
2012-07-15 12:25:27 +04:00
struct elf32_hdr * ehdr ;
2020-03-02 12:39:00 +03:00
u32 elf_shdr_get_size ;
u64 phoff , shoff ;
2012-07-15 12:25:27 +04:00
char class ;
2020-03-02 12:39:00 +03:00
u16 phnum ;
2012-07-15 12:25:27 +04:00
if ( ! fw ) {
dev_err ( dev , " failed to load %s \n " , name ) ;
return - EINVAL ;
}
if ( fw - > size < sizeof ( struct elf32_hdr ) ) {
dev_err ( dev , " Image is too small \n " ) ;
return - EINVAL ;
}
ehdr = ( struct elf32_hdr * ) fw - > data ;
2020-03-02 12:39:00 +03:00
if ( memcmp ( ehdr - > e_ident , ELFMAG , SELFMAG ) ) {
dev_err ( dev , " Image is corrupted (bad magic) \n " ) ;
return - EINVAL ;
}
2012-07-15 12:25:27 +04:00
class = ehdr - > e_ident [ EI_CLASS ] ;
2020-03-02 12:39:00 +03:00
if ( class ! = ELFCLASS32 & & class ! = ELFCLASS64 ) {
2012-07-15 12:25:27 +04:00
dev_err ( dev , " Unsupported class: %d \n " , class ) ;
return - EINVAL ;
}
2020-03-02 12:39:00 +03:00
if ( class = = ELFCLASS64 & & fw - > size < sizeof ( struct elf64_hdr ) ) {
dev_err ( dev , " elf64 header is too small \n " ) ;
return - EINVAL ;
}
2012-11-30 09:10:25 +04:00
/* We assume the firmware has the same endianness as the host */
2012-07-15 12:25:27 +04:00
# ifdef __LITTLE_ENDIAN
if ( ehdr - > e_ident [ EI_DATA ] ! = ELFDATA2LSB ) {
# else /* BIG ENDIAN */
if ( ehdr - > e_ident [ EI_DATA ] ! = ELFDATA2MSB ) {
# endif
2012-11-30 09:10:25 +04:00
dev_err ( dev , " Unsupported firmware endianness \n " ) ;
2012-07-15 12:25:27 +04:00
return - EINVAL ;
}
2020-03-02 12:39:00 +03:00
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 ) ;
2012-07-15 12:25:27 +04:00
2020-03-02 12:39:00 +03:00
if ( fw - > size < shoff + elf_shdr_get_size ) {
dev_err ( dev , " Image is too small \n " ) ;
2012-07-15 12:25:27 +04:00
return - EINVAL ;
}
2020-03-02 12:39:00 +03:00
if ( phnum = = 0 ) {
2012-07-15 12:25:27 +04:00
dev_err ( dev , " No loadable segments \n " ) ;
return - EINVAL ;
}
2020-03-02 12:39:00 +03:00
if ( phoff > fw - > size ) {
2012-07-15 12:25:27 +04:00
dev_err ( dev , " Firmware size is too small \n " ) ;
return - EINVAL ;
}
2020-03-02 12:39:00 +03:00
dev_dbg ( dev , " Firmware is an elf%d file \n " ,
class = = ELFCLASS32 ? 32 : 64 ) ;
2012-07-15 12:25:27 +04:00
return 0 ;
}
2020-03-02 12:39:00 +03:00
EXPORT_SYMBOL ( rproc_elf_sanity_check ) ;
2012-07-15 12:25:27 +04:00
/**
2012-06-19 11:08:18 +04:00
* rproc_elf_get_boot_addr ( ) - Get rproc ' s boot address .
2012-07-15 12:25:27 +04:00
* @ rproc : the remote processor handle
* @ fw : the ELF firmware image
*
* Note that the boot address is not a configurable property of all remote
* processors . Some will always boot at a specific hard - coded address .
2021-05-19 21:03:04 +03:00
*
* Return : entry point address of the ELF image
*
2012-07-15 12:25:27 +04:00
*/
2020-03-02 12:38:57 +03:00
u64 rproc_elf_get_boot_addr ( struct rproc * rproc , const struct firmware * fw )
2012-07-15 12:25:27 +04:00
{
2020-03-02 12:39:00 +03:00
return elf_hdr_get_e_entry ( fw_elf_get_class ( fw ) , fw - > data ) ;
2012-07-15 12:25:27 +04:00
}
2018-01-06 02:58:01 +03:00
EXPORT_SYMBOL ( rproc_elf_get_boot_addr ) ;
2012-07-15 12:25:27 +04:00
/**
2012-06-19 11:08:18 +04:00
* rproc_elf_load_segments ( ) - load firmware segments to memory
2012-07-15 12:25:27 +04:00
* @ rproc : remote processor which will be booted using these fw segments
* @ fw : the ELF firmware image
*
* This function loads the firmware segments to memory , where the remote
* processor expects them .
*
* Some remote processors will expect their code and data to be placed
* in specific device addresses , and can ' t have them dynamically assigned .
*
* We currently support only those kind of remote processors , and expect
* the program header ' s paddr member to contain those addresses . We then go
* through the physically contiguous " carveout " memory regions which we
* allocated ( and mapped ) earlier on behalf of the remote processor ,
* and " translate " device address to kernel addresses , so we can copy the
* segments where they are expected .
*
* Currently we only support remote processors that required carveout
* allocations and got them mapped onto their iommus . Some processors
* might be different : they might not have iommus , and would prefer to
* directly allocate memory for every segment / resource . This is not yet
* supported , though .
2021-05-19 21:03:04 +03:00
*
* Return : 0 on success and an appropriate error code otherwise
2012-07-15 12:25:27 +04:00
*/
2018-01-06 02:58:01 +03:00
int rproc_elf_load_segments ( struct rproc * rproc , const struct firmware * fw )
2012-07-15 12:25:27 +04:00
{
struct device * dev = & rproc - > dev ;
2020-03-02 12:39:00 +03:00
const void * ehdr , * phdr ;
2012-07-15 12:25:27 +04:00
int i , ret = 0 ;
2020-03-02 12:39:00 +03:00
u16 phnum ;
2012-07-15 12:25:27 +04:00
const u8 * elf_data = fw - > data ;
2020-03-02 12:39:00 +03:00
u8 class = fw_elf_get_class ( fw ) ;
u32 elf_phdr_get_size = elf_size_of_phdr ( class ) ;
2012-07-15 12:25:27 +04:00
2020-03-02 12:39:00 +03:00
ehdr = elf_data ;
phnum = elf_hdr_get_e_phnum ( class , ehdr ) ;
phdr = elf_data + elf_hdr_get_e_phoff ( class , ehdr ) ;
2012-07-15 12:25:27 +04:00
/* go through the available ELF segments */
2020-03-02 12:39:00 +03:00
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 ) ;
2012-07-15 12:25:27 +04:00
void * ptr ;
2021-03-06 14:24:19 +03:00
bool is_iomem ;
2012-07-15 12:25:27 +04:00
2020-03-02 12:39:00 +03:00
if ( type ! = PT_LOAD )
2012-07-15 12:25:27 +04:00
continue ;
2020-03-02 12:39:00 +03:00
dev_dbg ( dev , " phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx \n " ,
type , da , memsz , filesz ) ;
2012-07-15 12:25:27 +04:00
if ( filesz > memsz ) {
2020-03-02 12:39:00 +03:00
dev_err ( dev , " bad phdr filesz 0x%llx memsz 0x%llx \n " ,
2016-08-13 02:42:20 +03:00
filesz , memsz ) ;
2012-07-15 12:25:27 +04:00
ret = - EINVAL ;
break ;
}
if ( offset + filesz > fw - > size ) {
2020-03-02 12:39:00 +03:00
dev_err ( dev , " truncated fw: need 0x%llx avail 0x%zx \n " ,
2016-08-13 02:42:20 +03:00
offset + filesz , fw - > size ) ;
2012-07-15 12:25:27 +04:00
ret = - EINVAL ;
break ;
}
2020-03-02 12:39:00 +03:00
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 ;
}
2012-07-15 12:25:27 +04:00
/* grab the kernel address for this device address */
2021-03-06 14:24:19 +03:00
ptr = rproc_da_to_va ( rproc , da , memsz , & is_iomem ) ;
2012-07-15 12:25:27 +04:00
if ( ! ptr ) {
2020-03-02 12:39:00 +03:00
dev_err ( dev , " bad phdr da 0x%llx mem 0x%llx \n " , da ,
memsz ) ;
2012-07-15 12:25:27 +04:00
ret = - EINVAL ;
break ;
}
/* put the segment where the remote processor expects it */
2021-03-06 14:24:19 +03:00
if ( filesz ) {
if ( is_iomem )
memcpy_fromio ( ptr , ( void __iomem * ) ( elf_data + offset ) , filesz ) ;
else
memcpy ( ptr , elf_data + offset , filesz ) ;
}
2012-07-15 12:25:27 +04:00
/*
* Zero out remaining memory for this segment .
*
* This isn ' t strictly required since dma_alloc_coherent already
* did this for us . albeit harmless , we may consider removing
* this .
*/
2021-03-06 14:24:19 +03:00
if ( memsz > filesz ) {
if ( is_iomem )
memset_io ( ( void __iomem * ) ( ptr + filesz ) , 0 , memsz - filesz ) ;
else
memset ( ptr + filesz , 0 , memsz - filesz ) ;
}
2012-07-15 12:25:27 +04:00
}
return ret ;
}
2018-01-06 02:58:01 +03:00
EXPORT_SYMBOL ( rproc_elf_load_segments ) ;
2012-07-15 12:25:27 +04:00
2020-03-02 12:39:00 +03:00
static const void *
find_table ( struct device * dev , const struct firmware * fw )
2012-07-15 12:25:27 +04:00
{
2020-03-02 12:39:00 +03:00
const void * shdr , * name_table_shdr ;
2013-02-21 21:15:33 +04:00
int i ;
2012-07-15 12:25:27 +04:00
const char * name_table ;
struct resource_table * table = NULL ;
2020-03-02 12:39:00 +03:00
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 ) ;
2012-07-15 12:25:27 +04:00
2013-02-21 21:15:33 +04:00
/* look for the resource table and handle it */
2020-03-02 12:39:00 +03:00
/* 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 < 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 + name , " .resource_table " ) )
2012-07-15 12:25:27 +04:00
continue ;
table = ( struct resource_table * ) ( elf_data + offset ) ;
/* make sure we have the entire table */
2013-02-21 21:15:33 +04:00
if ( offset + size > fw_size | | offset + size < size ) {
2012-07-15 12:25:27 +04:00
dev_err ( dev , " resource table truncated \n " ) ;
return NULL ;
}
/* make sure table has at least the header */
if ( sizeof ( struct resource_table ) > size ) {
dev_err ( dev , " header-less resource table \n " ) ;
return NULL ;
}
/* we don't support any version beyond the first */
if ( table - > ver ! = 1 ) {
dev_err ( dev , " unsupported fw ver: %d \n " , table - > ver ) ;
return NULL ;
}
/* make sure reserved bytes are zeroes */
if ( table - > reserved [ 0 ] | | table - > reserved [ 1 ] ) {
dev_err ( dev , " non zero reserved bytes \n " ) ;
return NULL ;
}
/* make sure the offsets array isn't truncated */
2019-06-07 21:53:14 +03:00
if ( struct_size ( table , offset , table - > num ) > size ) {
2012-07-15 12:25:27 +04:00
dev_err ( dev , " resource table incomplete \n " ) ;
return NULL ;
}
2013-02-21 21:15:33 +04:00
return shdr ;
2012-07-15 12:25:27 +04:00
}
2013-02-21 21:15:33 +04:00
return NULL ;
}
/**
2018-01-06 02:58:03 +03:00
* rproc_elf_load_rsc_table ( ) - load the resource table
2013-02-21 21:15:33 +04:00
* @ rproc : the rproc handle
* @ fw : the ELF firmware image
*
* This function finds the resource table inside the remote processor ' s
2018-01-06 02:58:03 +03:00
* firmware , load it into the @ cached_table and update @ table_ptr .
2013-02-21 21:15:33 +04:00
*
2018-01-06 02:58:03 +03:00
* Return : 0 on success , negative errno on failure .
2013-02-21 21:15:33 +04:00
*/
2018-01-06 02:58:03 +03:00
int rproc_elf_load_rsc_table ( struct rproc * rproc , const struct firmware * fw )
2013-02-21 21:15:33 +04:00
{
2020-03-02 12:39:00 +03:00
const void * shdr ;
2013-02-21 21:15:33 +04:00
struct device * dev = & rproc - > dev ;
struct resource_table * table = NULL ;
const u8 * elf_data = fw - > data ;
2018-01-06 02:58:03 +03:00
size_t tablesz ;
2020-03-02 12:39:00 +03:00
u8 class = fw_elf_get_class ( fw ) ;
u64 sh_offset ;
2013-02-21 21:15:33 +04:00
2020-03-02 12:39:00 +03:00
shdr = find_table ( dev , fw ) ;
2013-02-21 21:15:33 +04:00
if ( ! shdr )
2018-01-06 02:58:03 +03:00
return - EINVAL ;
2013-02-21 21:15:33 +04:00
2020-03-02 12:39:00 +03:00
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 ) ;
2018-01-06 02:58:03 +03:00
/*
* Create a copy of the resource table . When a virtio device starts
* and calls vring_new_virtqueue ( ) the address of the allocated vring
* will be stored in the cached_table . Before the device is started ,
* cached_table will be copied into device memory .
*/
rproc - > cached_table = kmemdup ( table , tablesz , GFP_KERNEL ) ;
if ( ! rproc - > cached_table )
return - ENOMEM ;
2013-02-21 21:15:33 +04:00
2018-01-06 02:58:03 +03:00
rproc - > table_ptr = rproc - > cached_table ;
rproc - > table_sz = tablesz ;
return 0 ;
2012-07-15 12:25:27 +04:00
}
2018-01-06 02:58:03 +03:00
EXPORT_SYMBOL ( rproc_elf_load_rsc_table ) ;
2012-06-19 11:08:18 +04:00
2013-02-21 21:15:34 +04:00
/**
* rproc_elf_find_loaded_rsc_table ( ) - find the loaded resource table
* @ rproc : the rproc handle
* @ fw : the ELF firmware image
*
* This function finds the location of the loaded resource table . Don ' t
* call this function if the table wasn ' t loaded yet - it ' s a bug if you do .
*
2021-05-19 21:03:04 +03:00
* Return : pointer to the resource table if it is found or NULL otherwise .
2013-02-21 21:15:34 +04:00
* If the table wasn ' t loaded yet the result is unspecified .
*/
2018-01-06 02:58:01 +03:00
struct resource_table * rproc_elf_find_loaded_rsc_table ( struct rproc * rproc ,
const struct firmware * fw )
2013-02-21 21:15:34 +04:00
{
2020-03-02 12:39:00 +03:00
const void * shdr ;
u64 sh_addr , sh_size ;
u8 class = fw_elf_get_class ( fw ) ;
struct device * dev = & rproc - > dev ;
2013-02-21 21:15:34 +04:00
2020-03-02 12:39:00 +03:00
shdr = find_table ( & rproc - > dev , fw ) ;
2013-02-21 21:15:34 +04:00
if ( ! shdr )
return NULL ;
2020-03-02 12:39:00 +03:00
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 ;
}
2021-03-06 14:24:19 +03:00
return rproc_da_to_va ( rproc , sh_addr , sh_size , NULL ) ;
2013-02-21 21:15:34 +04:00
}
2018-01-06 02:58:01 +03:00
EXPORT_SYMBOL ( rproc_elf_find_loaded_rsc_table ) ;