sdk-hwV1.3/lichee/brandy-2.0/spl/common/elf_loader.c

162 lines
3.5 KiB
C

/*
* (C) Copyright 2013-2016
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
*
*/
#include <common.h>
#include <linux/types.h>
#include <linux/elf.h>
#ifdef CFG_ELF_64BIT
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Phdr Elf64_Phdr
#define Elf_Shdr Elf64_Shdr
#else
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Phdr Elf32_Phdr
#define Elf_Shdr Elf32_Shdr
#endif
/* copy from u-boot-2018/drivers/riscv/common/riscv_img.h */
struct vaddr_range_t {
unsigned long vstart;
unsigned long vend;
unsigned long pstart;
};
/*
* riscv need to remap addresses for some addr.
*/
static struct vaddr_range_t addr_mapping[] = {
#ifdef CFG_RISCV_E906
{ 0x3FFC0000, 0x4003FFFF, 0x07280000 },
{ 0x40400000, 0x7FFFFFFF, 0x40400000},
#endif
/*others (c906/e907) has the same addresses mapping as a7 */
};
static unsigned long set_img_va_to_pa(unsigned long vaddr,
struct vaddr_range_t *map, int size)
{
unsigned long paddr = vaddr;
int i;
for (i = 0; i < size; i++) {
if (vaddr >= map[i].vstart
&& vaddr <= map[i].vend) {
paddr = vaddr - map[i].vstart + map[i].pstart;
break;
}
}
return paddr;
}
phys_addr_t elf_get_entry_addr(phys_addr_t base)
{
Elf_Ehdr *ehdr;
ehdr = (Elf_Ehdr *)base;
return ehdr->e_entry;
}
int load_elf_image(phys_addr_t img_addr)
{
int i;
Elf_Ehdr *ehdr;
Elf_Phdr *phdr;
void *dst = NULL;
void *src = NULL;
int size = sizeof(addr_mapping) / sizeof(struct vaddr_range_t);
ehdr = (Elf_Ehdr *)img_addr;
phdr = (Elf_Phdr *)((phys_addr_t)(img_addr + ehdr->e_phoff));
/* load elf program segment */
for (i = 0; i < ehdr->e_phnum; ++i, ++phdr) {
//remap addresses
dst = (void *)(phys_addr_t)set_img_va_to_pa((unsigned long)phdr->p_paddr, \
addr_mapping, size);
src = (void *)((phys_addr_t)(img_addr + phdr->p_offset));
if (phdr->p_type != PT_LOAD)
continue;
if ((phdr->p_memsz == 0) || (phdr->p_filesz == 0))
continue;
if (phdr->p_filesz)
memcpy(dst, src, phdr->p_filesz);
if (phdr->p_filesz != phdr->p_memsz)
memset((u8 *)dst + phdr->p_filesz, 0x00,
phdr->p_memsz - phdr->p_filesz);
}
return 0;
}
static Elf_Shdr *elf_find_segment(phys_addr_t elf_addr, const char *seg_name)
{
int i;
Elf_Shdr *shdr;
Elf_Ehdr *ehdr;
const char *name_table;
const uint8_t *elf_data = (void *)elf_addr;
ehdr = (Elf_Ehdr *)elf_data;
shdr = (Elf_Shdr *)(elf_data + ehdr->e_shoff);
name_table = (const char *)(elf_data + shdr[ehdr->e_shstrndx].sh_offset);
for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
if (strcmp(name_table + shdr->sh_name, seg_name))
continue;
return shdr;
}
return NULL;
}
void *elf_find_segment_offset(phys_addr_t elf_addr, const char *seg_name)
{
Elf_Shdr *shdr;
shdr = elf_find_segment(elf_addr, seg_name);
if (!shdr)
return NULL;
return (void *)((phys_addr_t)(elf_addr + shdr->sh_offset));
}
void *elf_find_segment_addr(phys_addr_t elf_addr, const char *seg_name)
{
Elf_Shdr *shdr;
shdr = elf_find_segment(elf_addr, seg_name);
if (!shdr)
return NULL;
return (void *)((phys_addr_t)shdr->sh_addr);
}
#if defined(CFG_RISCV_E907) || defined(CFG_RISCV_E906) || defined(CFG_RISCV_C906)
void boot_riscv(phys_addr_t base, unsigned long fdt_addr)
{
#if defined(CFG_RISCV_E907)
void boot_e907(phys_addr_t base, unsigned long fdt_addr);
boot_e907(base, fdt_addr);
#elif defined(CFG_RISCV_E906)
void boot_e906(phys_addr_t base, unsigned long fdt_addr);
boot_e906(base, fdt_addr);
#elif defined(CFG_RISCV_C906)
void boot_c906(phys_addr_t base, unsigned long fdt_addr);
boot_c906(base, fdt_addr);
#endif
}
#endif