mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-19 03:15:00 +00:00

Do not load and flush segments whose program header type is not PT_LOAD to avoid crash happen. bootelf will load all program headers and then flush cache. Since PT_GNU_STACK start address is 0x0, crash happens when it flushes PT_GNU_STACK area for target that memory cannot be accessed from address 0x0. crash call stack: ==================================================================== bootelf -p |-> do_bootelf (cmd/elf.c) |-> load_elf_image_phdr (lib/elf.c) /* Load each program header */ |-> for (i = 0; i < ehdr->e_phnum; ++i) { /* e_phnum is 2 */ |-> flush_cache(start=0, size=0) (arch/arm/lib/cache.c) (*1) |-> flush_dcache_range(start=0, 0); (arch/arm/cpu/armv8/cache_v8.c) |-> __asm_flush_dcache_range(start=0, 0) (arch/arm/cpu/armv8/cache.S) |-> /* clean & invalidate data or unified cache */ |-> dc civac, x0 <<== crash happen x0:start=0 ==================================================================== *1 Here the p_paddr and p_filesz of the GNU_STACK program are 0. hello_world elf image program headers ==================================================================== Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000080ff0000 0x0000000080ff0000 0x0000000000010390 0x0000000000010390 R E 0x10000 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10 Section to Segment mapping: Segment Sections... 00 .text .rodata .eh_frame 01 ==================================================================== bootelf -p crash error log ==================================================================== bootelf -p 0x80f00000 "Synchronous Abort" handler, esr 0x96000147, far 0x0 elr: 000000008f6035bc lr : 000000008f66ff54 (reloc) elr: 00000000bd9475bc lr : 00000000bd9b3f54 x0 : 0000000000000000 x1 : 0000000000000000 x2 : 0000000000000040 x3 : 000000000000003f x4 : 0000000081000390 x5 : 00000000bd12da98 x6 : 0000000000000010 x7 : 0000000000000010 x8 : 000000000000000a x9 : 00000000bd156b30 x10: 00000000bd1526e0 x11: 0000000000000000 x12: 0000000000000004 x13: 00000000bda00000 x14: 00000000ffffffff x15: 00000000bd12dab7 x16: 00000000bd957b14 x17: 0000000000000000 x18: 00000000bd13fd90 x19: 0000000080f000b0 x20: 0000000080f00000 x21: 0000000000000002 x22: 0000000000000000 x23: 0000000000000003 x24: 00000000bd9eacc0 x25: 0000000000000000 x26: 0000000000000000 x27: 0000000000000000 x28: 00000000bd154c80 x29: 00000000bd12da70 Code: d2800082 9ac32042 d1000443 8a230000 (d50b7e20) ==================================================================== Signed-off-by: Jianfeng Zhu <JianfengA.Zhu@sony.com> Reviewed-by: Jacky Cao <Jacky.Cao@sony.com> Reviewed-by: Toyama, Yoshihiro <Yoshihiro.Toyama@sony.com>
307 lines
8.2 KiB
C
307 lines
8.2 KiB
C
// SPDX-License-Identifier: BSD-2-Clause
|
|
/*
|
|
Copyright (c) 2001 William L. Pitts
|
|
*/
|
|
|
|
#include <command.h>
|
|
#include <cpu_func.h>
|
|
#include <elf.h>
|
|
#include <env.h>
|
|
#include <errno.h>
|
|
#include <net.h>
|
|
#include <vxworks.h>
|
|
#ifdef CONFIG_X86
|
|
#include <vesa.h>
|
|
#include <asm/e820.h>
|
|
#include <linux/linkage.h>
|
|
#endif
|
|
|
|
/**
|
|
* bootelf_exec() - start the ELF image execution.
|
|
*
|
|
* @entry: address of entry point of ELF.
|
|
*
|
|
* May by used to allow ports to override the default behavior.
|
|
*/
|
|
unsigned long bootelf_exec(ulong (*entry)(int, char * const[]),
|
|
int argc, char *const argv[])
|
|
{
|
|
return entry(argc, argv);
|
|
}
|
|
|
|
/**
|
|
* bootelf() - Boot ELF from memory.
|
|
*
|
|
* @addr: Loading address of ELF in memory.
|
|
* @flags: Bits like ELF_PHDR to control boot details.
|
|
* @argc: May be used to pass command line arguments (maybe unused).
|
|
* Necessary for backward compatibility with the CLI command.
|
|
* If unused, must be 0.
|
|
* @argv: see @argc. If unused, must be NULL.
|
|
* Return: Number returned by ELF application.
|
|
*
|
|
* Sets errno = ENOEXEC if the ELF image is not valid.
|
|
*/
|
|
unsigned long bootelf(unsigned long addr, Bootelf_flags flags,
|
|
int argc, char *const argv[])
|
|
{
|
|
unsigned long entry_addr;
|
|
char *args[] = {"", NULL};
|
|
|
|
errno = 0;
|
|
|
|
if (!valid_elf_image(addr)) {
|
|
errno = ENOEXEC;
|
|
return 1;
|
|
}
|
|
|
|
entry_addr = flags.phdr ? load_elf_image_phdr(addr)
|
|
: load_elf_image_shdr(addr);
|
|
|
|
if (!flags.autostart)
|
|
return 0;
|
|
|
|
if (!argc && !argv) {
|
|
argc = 1;
|
|
argv = args;
|
|
}
|
|
|
|
return bootelf_exec((void *)entry_addr, argc, argv);
|
|
}
|
|
|
|
/*
|
|
* A very simple ELF64 loader, assumes the image is valid, returns the
|
|
* entry point address.
|
|
*
|
|
* Note if U-Boot is 32-bit, the loader assumes the to segment's
|
|
* physical address and size is within the lower 32-bit address space.
|
|
*/
|
|
unsigned long load_elf64_image_phdr(unsigned long addr)
|
|
{
|
|
Elf64_Ehdr *ehdr; /* Elf header structure pointer */
|
|
Elf64_Phdr *phdr; /* Program header structure pointer */
|
|
int i;
|
|
|
|
ehdr = (Elf64_Ehdr *)addr;
|
|
phdr = (Elf64_Phdr *)(addr + (ulong)ehdr->e_phoff);
|
|
|
|
/* Load each program header */
|
|
for (i = 0; i < ehdr->e_phnum; ++i) {
|
|
void *dst = (void *)(ulong)phdr->p_paddr;
|
|
void *src = (void *)addr + phdr->p_offset;
|
|
|
|
/* Only load PT_LOAD program header */
|
|
if (phdr->p_type != PT_LOAD)
|
|
continue;
|
|
|
|
debug("Loading phdr %i to 0x%p (%lu bytes)\n",
|
|
i, dst, (ulong)phdr->p_filesz);
|
|
if (phdr->p_filesz)
|
|
memcpy(dst, src, phdr->p_filesz);
|
|
if (phdr->p_filesz != phdr->p_memsz)
|
|
memset(dst + phdr->p_filesz, 0x00,
|
|
phdr->p_memsz - phdr->p_filesz);
|
|
flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN),
|
|
roundup(phdr->p_memsz, ARCH_DMA_MINALIGN));
|
|
++phdr;
|
|
}
|
|
|
|
if (ehdr->e_machine == EM_PPC64 && (ehdr->e_flags &
|
|
EF_PPC64_ELFV1_ABI)) {
|
|
/*
|
|
* For the 64-bit PowerPC ELF V1 ABI, e_entry is a function
|
|
* descriptor pointer with the first double word being the
|
|
* address of the entry point of the function.
|
|
*/
|
|
uintptr_t addr = ehdr->e_entry;
|
|
|
|
return *(Elf64_Addr *)addr;
|
|
}
|
|
|
|
return ehdr->e_entry;
|
|
}
|
|
|
|
unsigned long load_elf64_image_shdr(unsigned long addr)
|
|
{
|
|
Elf64_Ehdr *ehdr; /* Elf header structure pointer */
|
|
Elf64_Shdr *shdr; /* Section header structure pointer */
|
|
unsigned char *strtab = 0; /* String table pointer */
|
|
unsigned char *image; /* Binary image pointer */
|
|
int i; /* Loop counter */
|
|
|
|
ehdr = (Elf64_Ehdr *)addr;
|
|
|
|
/* Find the section header string table for output info */
|
|
shdr = (Elf64_Shdr *)(addr + (ulong)ehdr->e_shoff +
|
|
(ehdr->e_shstrndx * sizeof(Elf64_Shdr)));
|
|
|
|
if (shdr->sh_type == SHT_STRTAB)
|
|
strtab = (unsigned char *)(addr + (ulong)shdr->sh_offset);
|
|
|
|
/* Load each appropriate section */
|
|
for (i = 0; i < ehdr->e_shnum; ++i) {
|
|
shdr = (Elf64_Shdr *)(addr + (ulong)ehdr->e_shoff +
|
|
(i * sizeof(Elf64_Shdr)));
|
|
|
|
if (!(shdr->sh_flags & SHF_ALLOC) ||
|
|
shdr->sh_addr == 0 || shdr->sh_size == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (strtab) {
|
|
debug("%sing %s @ 0x%08lx (%ld bytes)\n",
|
|
(shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load",
|
|
&strtab[shdr->sh_name],
|
|
(unsigned long)shdr->sh_addr,
|
|
(long)shdr->sh_size);
|
|
}
|
|
|
|
if (shdr->sh_type == SHT_NOBITS) {
|
|
memset((void *)(uintptr_t)shdr->sh_addr, 0,
|
|
shdr->sh_size);
|
|
} else {
|
|
image = (unsigned char *)addr + (ulong)shdr->sh_offset;
|
|
memcpy((void *)(uintptr_t)shdr->sh_addr,
|
|
(const void *)image, shdr->sh_size);
|
|
}
|
|
flush_cache(rounddown(shdr->sh_addr, ARCH_DMA_MINALIGN),
|
|
roundup((shdr->sh_addr + shdr->sh_size),
|
|
ARCH_DMA_MINALIGN) -
|
|
rounddown(shdr->sh_addr, ARCH_DMA_MINALIGN));
|
|
}
|
|
|
|
if (ehdr->e_machine == EM_PPC64 && (ehdr->e_flags &
|
|
EF_PPC64_ELFV1_ABI)) {
|
|
/*
|
|
* For the 64-bit PowerPC ELF V1 ABI, e_entry is a function
|
|
* descriptor pointer with the first double word being the
|
|
* address of the entry point of the function.
|
|
*/
|
|
uintptr_t addr = ehdr->e_entry;
|
|
|
|
return *(Elf64_Addr *)addr;
|
|
}
|
|
|
|
return ehdr->e_entry;
|
|
}
|
|
|
|
/*
|
|
* A very simple ELF loader, assumes the image is valid, returns the
|
|
* entry point address.
|
|
*
|
|
* The loader firstly reads the EFI class to see if it's a 64-bit image.
|
|
* If yes, call the ELF64 loader. Otherwise continue with the ELF32 loader.
|
|
*/
|
|
unsigned long load_elf_image_phdr(unsigned long addr)
|
|
{
|
|
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
|
|
Elf32_Phdr *phdr; /* Program header structure pointer */
|
|
int i;
|
|
|
|
ehdr = (Elf32_Ehdr *)addr;
|
|
if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
|
|
return load_elf64_image_phdr(addr);
|
|
|
|
phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
|
|
|
|
/* Load each program header */
|
|
for (i = 0; i < ehdr->e_phnum; ++i) {
|
|
void *dst = (void *)(uintptr_t)phdr->p_paddr;
|
|
void *src = (void *)addr + phdr->p_offset;
|
|
|
|
/* Only load PT_LOAD program header */
|
|
if (phdr->p_type != PT_LOAD)
|
|
continue;
|
|
|
|
debug("Loading phdr %i to 0x%p (%i bytes)\n",
|
|
i, dst, phdr->p_filesz);
|
|
if (phdr->p_filesz)
|
|
memcpy(dst, src, phdr->p_filesz);
|
|
if (phdr->p_filesz != phdr->p_memsz)
|
|
memset(dst + phdr->p_filesz, 0x00,
|
|
phdr->p_memsz - phdr->p_filesz);
|
|
flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN),
|
|
roundup(phdr->p_memsz, ARCH_DMA_MINALIGN));
|
|
++phdr;
|
|
}
|
|
|
|
return ehdr->e_entry;
|
|
}
|
|
|
|
unsigned long load_elf_image_shdr(unsigned long addr)
|
|
{
|
|
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
|
|
Elf32_Shdr *shdr; /* Section header structure pointer */
|
|
unsigned char *strtab = 0; /* String table pointer */
|
|
unsigned char *image; /* Binary image pointer */
|
|
int i; /* Loop counter */
|
|
|
|
ehdr = (Elf32_Ehdr *)addr;
|
|
if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
|
|
return load_elf64_image_shdr(addr);
|
|
|
|
/* Find the section header string table for output info */
|
|
shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
|
|
(ehdr->e_shstrndx * sizeof(Elf32_Shdr)));
|
|
|
|
if (shdr->sh_type == SHT_STRTAB)
|
|
strtab = (unsigned char *)(addr + shdr->sh_offset);
|
|
|
|
/* Load each appropriate section */
|
|
for (i = 0; i < ehdr->e_shnum; ++i) {
|
|
shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
|
|
(i * sizeof(Elf32_Shdr)));
|
|
|
|
if (!(shdr->sh_flags & SHF_ALLOC) ||
|
|
shdr->sh_addr == 0 || shdr->sh_size == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (strtab) {
|
|
debug("%sing %s @ 0x%08lx (%ld bytes)\n",
|
|
(shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load",
|
|
&strtab[shdr->sh_name],
|
|
(unsigned long)shdr->sh_addr,
|
|
(long)shdr->sh_size);
|
|
}
|
|
|
|
if (shdr->sh_type == SHT_NOBITS) {
|
|
memset((void *)(uintptr_t)shdr->sh_addr, 0,
|
|
shdr->sh_size);
|
|
} else {
|
|
image = (unsigned char *)addr + shdr->sh_offset;
|
|
memcpy((void *)(uintptr_t)shdr->sh_addr,
|
|
(const void *)image, shdr->sh_size);
|
|
}
|
|
flush_cache(rounddown(shdr->sh_addr, ARCH_DMA_MINALIGN),
|
|
roundup((shdr->sh_addr + shdr->sh_size),
|
|
ARCH_DMA_MINALIGN) -
|
|
rounddown(shdr->sh_addr, ARCH_DMA_MINALIGN));
|
|
}
|
|
|
|
return ehdr->e_entry;
|
|
}
|
|
|
|
/*
|
|
* Determine if a valid ELF image exists at the given memory location.
|
|
* First look at the ELF header magic field, then make sure that it is
|
|
* executable.
|
|
*/
|
|
int valid_elf_image(unsigned long addr)
|
|
{
|
|
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
|
|
|
|
ehdr = (Elf32_Ehdr *)addr;
|
|
|
|
if (!IS_ELF(*ehdr)) {
|
|
printf("## No elf image at address 0x%08lx\n", addr);
|
|
return 0;
|
|
}
|
|
|
|
if (ehdr->e_type != ET_EXEC) {
|
|
printf("## Not a 32-bit elf image at address 0x%08lx\n", addr);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|