#include <sys/types.h>
#include <sys/inttypes.h>
#include <sys/systm.h>
#include <sys/elf.h>
#include <sys/elf_notes.h>
#include <util/memcpy.h>
#include "dboot_xboot.h"
#include "dboot_elfload.h"
#include "dboot_printf.h"
static caddr_t elf_file = 0;
#define PGETBYTES(offset) ((void *)(elf_file + (offset)))
static void *
getehdr(void)
{
uchar_t *ident;
void *hdr = NULL;
ident = PGETBYTES(0);
if (ident == NULL)
dboot_panic("Cannot read kernel ELF header");
if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3)
dboot_panic("not an ELF file!");
if (ident[EI_CLASS] == ELFCLASS32)
hdr = PGETBYTES(0);
else if (ident[EI_CLASS] == ELFCLASS64)
hdr = PGETBYTES(0);
else
dboot_panic("Unknown ELF class");
return (hdr);
}
int
dboot_elfload64(uintptr_t file_image)
{
Elf64_Ehdr *eh;
Elf64_Phdr *phdr;
caddr_t allphdrs;
int i;
paddr_t src;
paddr_t dst;
elf_file = (caddr_t)file_image;
allphdrs = NULL;
eh = getehdr();
if (eh == NULL)
dboot_panic("getehdr() failed");
if (eh->e_type != ET_EXEC)
dboot_panic("not ET_EXEC, e_type = 0x%x", eh->e_type);
if (eh->e_phnum == 0 || eh->e_phoff == 0)
dboot_panic("no program headers");
allphdrs = PGETBYTES(eh->e_phoff);
if (allphdrs == NULL)
dboot_panic("Failed to get program headers e_phnum = %d",
eh->e_phnum);
for (i = 0; i < eh->e_phnum; i++) {
phdr = (Elf64_Phdr *)(allphdrs + eh->e_phentsize * i);
if (phdr->p_type == PT_INTERP) {
dboot_printf("warning: PT_INTERP section\n");
continue;
}
if (phdr->p_type != PT_LOAD)
continue;
if (phdr->p_flags == (PF_R | PF_W) && phdr->p_vaddr == 0) {
dboot_printf("warning: krtld reloc info?\n");
continue;
}
if (phdr->p_memsz == 0)
continue;
if (phdr->p_paddr == phdr->p_vaddr) {
if (prom_debug)
dboot_printf("Skipping PT_LOAD segment for "
"paddr = 0x%lx\n", (ulong_t)phdr->p_paddr);
continue;
}
if (phdr->p_paddr != FOUR_MEG && phdr->p_paddr != 2 * FOUR_MEG)
dboot_panic("Bad paddr for kernel nucleus segment");
src = (uintptr_t)PGETBYTES(phdr->p_offset);
dst = ktext_phys + phdr->p_paddr - FOUR_MEG;
if (prom_debug)
dboot_printf("copying %ld bytes from ELF offset 0x%lx "
"to physaddr 0x%lx (va=0x%lx)\n",
(ulong_t)phdr->p_filesz, (ulong_t)phdr->p_offset,
(ulong_t)dst, (ulong_t)phdr->p_vaddr);
(void) memcpy((void *)(uintptr_t)dst,
(void *)(uintptr_t)src, (size_t)phdr->p_filesz);
if (phdr->p_filesz < phdr->p_memsz) {
if (prom_debug) {
dboot_printf("zeroing BSS %zu bytes from "
"physaddr 0x%" PRIx64
" (end=0x%" PRIx64 ")\n",
(size_t)(phdr->p_memsz - phdr->p_filesz),
dst + phdr->p_filesz,
dst + phdr->p_memsz - 1);
}
(void) memset((void *)(uintptr_t)(dst + phdr->p_filesz),
0, phdr->p_memsz - phdr->p_filesz);
}
}
return (0);
}