mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +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>
		
	
			
		
			
				
	
	
		
			308 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			308 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;
 | 
						|
}
 |