mirror of
https://github.com/xcat2/xNBA.git
synced 2024-12-14 15:21:32 +00:00
698 lines
21 KiB
C
698 lines
21 KiB
C
#include "elf.h"
|
|
|
|
#ifndef ELF_CHECK_ARCH
|
|
#error ELF_CHECK_ARCH not defined
|
|
#endif
|
|
|
|
#define ELF_NOTES 1
|
|
#define ELF_DEBUG 0
|
|
|
|
struct elf_state
|
|
{
|
|
union {
|
|
Elf32_Ehdr elf32;
|
|
Elf64_Ehdr elf64;
|
|
} e;
|
|
union {
|
|
Elf32_Phdr phdr32[1];
|
|
Elf64_Phdr phdr64[1];
|
|
unsigned char dummy[1024];
|
|
} p;
|
|
unsigned long curaddr;
|
|
int segment; /* current segment number, -1 for none */
|
|
uint64_t loc; /* start offset of current block */
|
|
uint64_t skip; /* padding to be skipped to current segment */
|
|
unsigned long toread; /* remaining data to be read in the segment */
|
|
#if ELF_NOTES
|
|
int check_ip_checksum;
|
|
uint16_t ip_checksum;
|
|
unsigned long ip_checksum_offset;
|
|
#endif
|
|
};
|
|
|
|
static struct elf_state estate;
|
|
|
|
static unsigned long find_segment(unsigned long size, unsigned long align)
|
|
{
|
|
unsigned i;
|
|
/* Verify I have a power of 2 alignment */
|
|
if (align & (align - 1)) {
|
|
return ULONG_MAX;
|
|
}
|
|
for(i = 0; i < meminfo.map_count; i++) {
|
|
unsigned long r_start, r_end;
|
|
if (meminfo.map[i].type != E820_RAM)
|
|
continue;
|
|
if ((meminfo.map[i].addr + meminfo.map[i].size) > ULONG_MAX) {
|
|
continue;
|
|
}
|
|
r_start = meminfo.map[i].addr;
|
|
r_end = r_start + meminfo.map[i].size;
|
|
/* Don't allow the segment to overlap etherboot */
|
|
if ((r_end > virt_to_phys(_text)) && (r_start < virt_to_phys(_text))) {
|
|
r_end = virt_to_phys(_text);
|
|
}
|
|
if ((r_start > virt_to_phys(_text)) && (r_start < virt_to_phys(_end))) {
|
|
r_start = virt_to_phys(_end);
|
|
}
|
|
/* Don't allow the segment to overlap the heap */
|
|
if ((r_end > heap_ptr) && (r_start < heap_ptr)) {
|
|
r_end = heap_ptr;
|
|
}
|
|
if ((r_start > heap_ptr) && (r_start < heap_bot)) {
|
|
r_start = heap_ptr;
|
|
}
|
|
r_start = (r_start + align - 1) & ~(align - 1);
|
|
if ((r_end >= r_start) && ((r_end - r_start) >= size)) {
|
|
return r_start;
|
|
}
|
|
}
|
|
/* I did not find anything :( */
|
|
return ULONG_MAX;
|
|
}
|
|
|
|
static void elf_boot(unsigned long machine, unsigned long entry)
|
|
{
|
|
int result;
|
|
struct Elf_Bhdr *hdr;
|
|
multiboot_boot(entry);
|
|
/* We cleanup unconditionally, and then reawaken the network
|
|
* adapter after the longjmp.
|
|
*/
|
|
hdr = prepare_boot_params(&estate.e);
|
|
result = elf_start(machine, entry, virt_to_phys(hdr));
|
|
if (result == 0) {
|
|
result = -1;
|
|
}
|
|
printf("Secondary program returned %d\n", result);
|
|
longjmp(restart_etherboot, result);
|
|
}
|
|
|
|
#if ELF_NOTES
|
|
static int elf_prep_segment(
|
|
unsigned long start __unused, unsigned long mid __unused, unsigned long end __unused,
|
|
unsigned long istart, unsigned long iend)
|
|
|
|
{
|
|
if (estate.check_ip_checksum) {
|
|
if ((istart <= estate.ip_checksum_offset) &&
|
|
(iend > estate.ip_checksum_offset)) {
|
|
/* The checksum note is also loaded in a
|
|
* PT_LOAD segment, so the computed checksum
|
|
* should be 0.
|
|
*/
|
|
estate.ip_checksum = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
#else
|
|
#define elf_prep_segment(start, mid, end, istart, iend) (1)
|
|
#endif
|
|
|
|
|
|
#if ELF_NOTES
|
|
static void process_elf_notes(unsigned char *header,
|
|
unsigned long offset, unsigned long length)
|
|
{
|
|
unsigned char *note, *end;
|
|
char *program, *version;
|
|
|
|
estate.check_ip_checksum = 0;
|
|
note = header + offset;
|
|
end = note + length;
|
|
program = version = 0;
|
|
while(note < end) {
|
|
Elf_Nhdr *hdr;
|
|
unsigned char *n_name, *n_desc, *next;
|
|
hdr = (Elf_Nhdr *)note;
|
|
n_name = note + sizeof(*hdr);
|
|
n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
|
|
next = n_desc + ((hdr->n_descsz + 3) & ~3);
|
|
if (next > end) {
|
|
break;
|
|
}
|
|
if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) &&
|
|
(memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) {
|
|
switch(hdr->n_type) {
|
|
case EIN_PROGRAM_NAME:
|
|
if (n_desc[hdr->n_descsz -1] == 0) {
|
|
program = n_desc;
|
|
}
|
|
break;
|
|
case EIN_PROGRAM_VERSION:
|
|
if (n_desc[hdr->n_descsz -1] == 0) {
|
|
version = n_desc;
|
|
}
|
|
break;
|
|
case EIN_PROGRAM_CHECKSUM:
|
|
estate.check_ip_checksum = 1;
|
|
estate.ip_checksum = *((uint16_t *)n_desc);
|
|
/* Remember where the segment is so
|
|
* I can detect segment overlaps.
|
|
*/
|
|
estate.ip_checksum_offset = n_desc - header;
|
|
#if ELF_DEBUG
|
|
printf("Checksum: %hx\n", estate.ip_checksum);
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
}
|
|
#if ELF_DEBUG
|
|
printf("n_type: %x n_name(%d): %s n_desc(%d): %s\n",
|
|
hdr->n_type,
|
|
hdr->n_namesz, n_name,
|
|
hdr->n_descsz, n_desc);
|
|
#endif
|
|
note = next;
|
|
}
|
|
if (program && version) {
|
|
printf("\nLoading %s version: %s\n", program, version);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef ELF_IMAGE
|
|
static sector_t elf32_download(unsigned char *data, unsigned int len, int eof);
|
|
static inline os_download_t elf32_probe(unsigned char *data, unsigned int len)
|
|
{
|
|
unsigned long phdr_size;
|
|
if (len < sizeof(estate.e.elf32)) {
|
|
return 0;
|
|
}
|
|
memcpy(&estate.e.elf32, data, sizeof(estate.e.elf32));
|
|
if ((estate.e.elf32.e_ident[EI_MAG0] != ELFMAG0) ||
|
|
(estate.e.elf32.e_ident[EI_MAG1] != ELFMAG1) ||
|
|
(estate.e.elf32.e_ident[EI_MAG2] != ELFMAG2) ||
|
|
(estate.e.elf32.e_ident[EI_MAG3] != ELFMAG3) ||
|
|
(estate.e.elf32.e_ident[EI_CLASS] != ELFCLASS32) ||
|
|
(estate.e.elf32.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
|
|
(estate.e.elf32.e_ident[EI_VERSION] != EV_CURRENT) ||
|
|
( (estate.e.elf32.e_type != ET_EXEC) &&
|
|
(estate.e.elf32.e_type != ET_DYN)) ||
|
|
(estate.e.elf32.e_version != EV_CURRENT) ||
|
|
(estate.e.elf32.e_ehsize != sizeof(Elf32_Ehdr)) ||
|
|
(estate.e.elf32.e_phentsize != sizeof(Elf32_Phdr)) ||
|
|
!ELF_CHECK_ARCH(estate.e.elf32)) {
|
|
return 0;
|
|
}
|
|
printf("(ELF");
|
|
elf_freebsd_probe();
|
|
multiboot_probe(data, len);
|
|
printf(")... ");
|
|
phdr_size = estate.e.elf32.e_phnum * estate.e.elf32.e_phentsize;
|
|
if (estate.e.elf32.e_phoff + phdr_size > len) {
|
|
printf("ELF header outside first block\n");
|
|
return dead_download;
|
|
}
|
|
if (phdr_size > sizeof(estate.p.dummy)) {
|
|
printf("Program header to big\n");
|
|
return dead_download;
|
|
}
|
|
memcpy(&estate.p.phdr32, data + estate.e.elf32.e_phoff, phdr_size);
|
|
if (estate.e.elf32.e_type == ET_DYN) {
|
|
Elf32_Addr min, max, base_addr, delta, align;
|
|
min = -1;
|
|
max = 0;
|
|
align = 1;
|
|
for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
|
Elf32_Addr val;
|
|
if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
|
|
continue;
|
|
val = estate.p.phdr32[estate.segment].p_paddr;
|
|
if (val < min) {
|
|
min = val;
|
|
}
|
|
val += estate.p.phdr32[estate.segment].p_memsz;
|
|
if (val > max) {
|
|
max = val;
|
|
}
|
|
if (estate.p.phdr32[estate.segment].p_align > align) {
|
|
align = estate.p.phdr32[estate.segment].p_align;
|
|
}
|
|
}
|
|
if (align & (align -1)) {
|
|
printf("ELF base address alignment is not a power of 2\n");
|
|
return dead_download;
|
|
}
|
|
base_addr = find_segment(max - min, align);
|
|
if (base_addr == ULONG_MAX) {
|
|
printf("ELF base address not available for size %ld\n", max - min);
|
|
return dead_download;
|
|
}
|
|
/* Compute the change in base address and fix up the addresses */
|
|
delta = base_addr - min;
|
|
for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
|
/* Change the base address of the object to load */
|
|
estate.p.phdr32[estate.segment].p_paddr += delta;
|
|
}
|
|
estate.e.elf32.e_entry += delta;
|
|
}
|
|
#if ELF_NOTES
|
|
/* Load ELF notes from the image */
|
|
for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
|
if (estate.p.phdr32[estate.segment].p_type != PT_NOTE)
|
|
continue;
|
|
if (estate.p.phdr32[estate.segment].p_offset + estate.p.phdr32[estate.segment].p_filesz > len) {
|
|
/* Ignore ELF notes outside of the first block */
|
|
continue;
|
|
}
|
|
process_elf_notes(data,
|
|
estate.p.phdr32[estate.segment].p_offset, estate.p.phdr32[estate.segment].p_filesz);
|
|
}
|
|
#endif
|
|
/* Check for Etherboot related limitations. Memory
|
|
* between _text and _end is not allowed.
|
|
* Reasons: the Etherboot code/data area.
|
|
*/
|
|
for (estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
|
unsigned long start, mid, end, istart, iend;
|
|
if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
|
|
continue;
|
|
|
|
elf_freebsd_fixup_segment();
|
|
|
|
start = estate.p.phdr32[estate.segment].p_paddr;
|
|
mid = start + estate.p.phdr32[estate.segment].p_filesz;
|
|
end = start + estate.p.phdr32[estate.segment].p_memsz;
|
|
istart = estate.p.phdr32[estate.segment].p_offset;
|
|
iend = istart + estate.p.phdr32[estate.segment].p_filesz;
|
|
if (!prep_segment(start, mid, end, istart, iend)) {
|
|
return dead_download;
|
|
}
|
|
if (!elf_prep_segment(start, mid, end, istart, iend)) {
|
|
return dead_download;
|
|
}
|
|
}
|
|
estate.segment = -1;
|
|
estate.loc = 0;
|
|
estate.skip = 0;
|
|
estate.toread = 0;
|
|
return elf32_download;
|
|
}
|
|
|
|
static sector_t elf32_download(unsigned char *data, unsigned int len, int eof)
|
|
{
|
|
unsigned long skip_sectors = 0;
|
|
unsigned int offset; /* working offset in the current data block */
|
|
int i;
|
|
|
|
offset = 0;
|
|
do {
|
|
if (estate.segment != -1) {
|
|
if (estate.skip) {
|
|
if (estate.skip >= len - offset) {
|
|
estate.skip -= len - offset;
|
|
break;
|
|
}
|
|
offset += estate.skip;
|
|
estate.skip = 0;
|
|
}
|
|
|
|
if (estate.toread) {
|
|
unsigned int cplen;
|
|
cplen = len - offset;
|
|
if (cplen >= estate.toread) {
|
|
cplen = estate.toread;
|
|
}
|
|
memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
|
|
estate.curaddr += cplen;
|
|
estate.toread -= cplen;
|
|
offset += cplen;
|
|
if (estate.toread)
|
|
break;
|
|
elf_freebsd_find_segment_end();
|
|
}
|
|
}
|
|
|
|
/* Data left, but current segment finished - look for the next
|
|
* segment (in file offset order) that needs to be loaded.
|
|
* We can only seek forward, so select the program headers,
|
|
* in the correct order.
|
|
*/
|
|
estate.segment = -1;
|
|
for (i = 0; i < estate.e.elf32.e_phnum; i++) {
|
|
if (estate.p.phdr32[i].p_type != PT_LOAD)
|
|
continue;
|
|
if (estate.p.phdr32[i].p_filesz == 0)
|
|
continue;
|
|
if (estate.p.phdr32[i].p_offset < estate.loc + offset)
|
|
continue; /* can't go backwards */
|
|
if ((estate.segment != -1) &&
|
|
(estate.p.phdr32[i].p_offset >= estate.p.phdr32[estate.segment].p_offset))
|
|
continue; /* search minimum file offset */
|
|
estate.segment = i;
|
|
}
|
|
if (estate.segment == -1) {
|
|
if (elf_freebsd_debug_loader(offset)) {
|
|
estate.segment = 0; /* -1 makes it not read anymore */
|
|
continue;
|
|
}
|
|
/* No more segments to be loaded, so just start the
|
|
* kernel. This saves a lot of network bandwidth if
|
|
* debug info is in the kernel but not loaded. */
|
|
goto elf_startkernel;
|
|
break;
|
|
}
|
|
estate.curaddr = estate.p.phdr32[estate.segment].p_paddr;
|
|
estate.skip = estate.p.phdr32[estate.segment].p_offset - (estate.loc + offset);
|
|
estate.toread = estate.p.phdr32[estate.segment].p_filesz;
|
|
#if ELF_DEBUG
|
|
printf("PHDR %d, size %#lX, curaddr %#lX\n",
|
|
estate.segment, estate.toread, estate.curaddr);
|
|
#endif
|
|
} while (offset < len);
|
|
|
|
estate.loc += len + (estate.skip & ~0x1ff);
|
|
skip_sectors = estate.skip >> 9;
|
|
estate.skip &= 0x1ff;
|
|
|
|
if (eof) {
|
|
unsigned long entry;
|
|
unsigned long machine;
|
|
elf_startkernel:
|
|
entry = estate.e.elf32.e_entry;
|
|
machine = estate.e.elf32.e_machine;
|
|
|
|
#if ELF_NOTES
|
|
if (estate.check_ip_checksum) {
|
|
unsigned long bytes = 0;
|
|
uint16_t sum, new_sum;
|
|
|
|
sum = ipchksum(&estate.e.elf32, sizeof(estate.e.elf32));
|
|
bytes = sizeof(estate.e.elf32);
|
|
#if ELF_DEBUG
|
|
printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
|
|
sum, sum, bytes, bytes);
|
|
#endif
|
|
|
|
new_sum = ipchksum(estate.p.phdr32, sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum);
|
|
sum = add_ipchksums(bytes, sum, new_sum);
|
|
bytes += sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum;
|
|
#if ELF_DEBUG
|
|
printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
|
|
new_sum, sum,
|
|
sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum, bytes);
|
|
#endif
|
|
|
|
for(i = 0; i < estate.e.elf32.e_phnum; i++) {
|
|
if (estate.p.phdr32[i].p_type != PT_LOAD)
|
|
continue;
|
|
new_sum = ipchksum(phys_to_virt(estate.p.phdr32[i].p_paddr),
|
|
estate.p.phdr32[i].p_memsz);
|
|
sum = add_ipchksums(bytes, sum, new_sum);
|
|
bytes += estate.p.phdr32[i].p_memsz;
|
|
#if ELF_DEBUG
|
|
printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
|
|
i, new_sum, sum,
|
|
estate.p.phdr32[i].p_memsz, bytes);
|
|
#endif
|
|
|
|
}
|
|
if (estate.ip_checksum != sum) {
|
|
printf("\nImage checksum: %hx != computed checksum: %hx\n",
|
|
estate.ip_checksum, sum);
|
|
longjmp(restart_etherboot, -2);
|
|
}
|
|
}
|
|
#endif
|
|
done(1);
|
|
/* Fixup the offset to the program header so you can find the program headers from
|
|
* the ELF header mknbi needs this.
|
|
*/
|
|
estate.e.elf32.e_phoff = (char *)&estate.p - (char *)&estate.e;
|
|
elf_freebsd_boot(entry);
|
|
elf_boot(machine,entry);
|
|
}
|
|
return skip_sectors;
|
|
}
|
|
#endif /* ELF_IMAGE */
|
|
|
|
#ifdef ELF64_IMAGE
|
|
static sector_t elf64_download(unsigned char *data, unsigned int len, int eof);
|
|
static inline os_download_t elf64_probe(unsigned char *data, unsigned int len)
|
|
{
|
|
unsigned long phdr_size;
|
|
if (len < sizeof(estate.e.elf64)) {
|
|
return 0;
|
|
}
|
|
memcpy(&estate.e.elf64, data, sizeof(estate.e.elf64));
|
|
if ((estate.e.elf64.e_ident[EI_MAG0] != ELFMAG0) ||
|
|
(estate.e.elf64.e_ident[EI_MAG1] != ELFMAG1) ||
|
|
(estate.e.elf64.e_ident[EI_MAG2] != ELFMAG2) ||
|
|
(estate.e.elf64.e_ident[EI_MAG3] != ELFMAG3) ||
|
|
(estate.e.elf64.e_ident[EI_CLASS] != ELFCLASS64) ||
|
|
(estate.e.elf64.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
|
|
(estate.e.elf64.e_ident[EI_VERSION] != EV_CURRENT) ||
|
|
( (estate.e.elf64.e_type != ET_EXEC) &&
|
|
(estate.e.elf64.e_type != ET_DYN)) ||
|
|
(estate.e.elf64.e_version != EV_CURRENT) ||
|
|
(estate.e.elf64.e_ehsize != sizeof(Elf64_Ehdr)) ||
|
|
(estate.e.elf64.e_phentsize != sizeof(Elf64_Phdr)) ||
|
|
!ELF_CHECK_ARCH(estate.e.elf64)) {
|
|
return 0;
|
|
}
|
|
printf("(ELF64)... ");
|
|
phdr_size = estate.e.elf64.e_phnum * estate.e.elf64.e_phentsize;
|
|
if (estate.e.elf64.e_phoff + phdr_size > len) {
|
|
printf("ELF header outside first block\n");
|
|
return dead_download;
|
|
}
|
|
if (phdr_size > sizeof(estate.p.dummy)) {
|
|
printf("Program header to big\n");
|
|
return dead_download;
|
|
}
|
|
if (estate.e.elf64.e_entry > ULONG_MAX) {
|
|
printf("ELF entry point exceeds address space\n");
|
|
return dead_download;
|
|
}
|
|
memcpy(&estate.p.phdr64, data + estate.e.elf64.e_phoff, phdr_size);
|
|
if (estate.e.elf64.e_type == ET_DYN) {
|
|
Elf64_Addr min, max, base_addr, delta, align;
|
|
min = -1;
|
|
max = 0;
|
|
align = 1;
|
|
for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
|
Elf64_Addr val;
|
|
if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
|
|
continue;
|
|
val = estate.p.phdr64[estate.segment].p_paddr;
|
|
if (val < min) {
|
|
min = val;
|
|
}
|
|
val += estate.p.phdr64[estate.segment].p_memsz;
|
|
if (val > max) {
|
|
max = val;
|
|
}
|
|
if (estate.p.phdr64[estate.segment].p_align > align) {
|
|
align = estate.p.phdr64[estate.segment].p_align;
|
|
}
|
|
}
|
|
if (align > ULONG_MAX) {
|
|
printf("ELF base address alignment exceeds address space\n");
|
|
return dead_download;
|
|
}
|
|
if (align & (align -1)) {
|
|
printf("ELF base address alignment is not a power of 2\n");
|
|
return dead_download;
|
|
}
|
|
if ((max - min) > ULONG_MAX) {
|
|
printf("ELF size exceeds address space\n");
|
|
return dead_download;
|
|
}
|
|
base_addr = find_segment(max - min, align);
|
|
if (base_addr == ULONG_MAX) {
|
|
printf("ELF base address not available for size %ld\n", max - min);
|
|
return dead_download;
|
|
}
|
|
/* Compute the change in base address and fix up the addresses */
|
|
delta = base_addr - min;
|
|
for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
|
/* Change the base address of the object to load */
|
|
estate.p.phdr64[estate.segment].p_paddr += delta;
|
|
}
|
|
estate.e.elf64.e_entry += delta;
|
|
}
|
|
#if ELF_NOTES
|
|
/* Load ELF notes from the image */
|
|
for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
|
if (estate.p.phdr64[estate.segment].p_type != PT_NOTE)
|
|
continue;
|
|
if (estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz > len) {
|
|
/* Ignore ELF notes outside of the first block */
|
|
continue;
|
|
}
|
|
process_elf_notes(data,
|
|
estate.p.phdr64[estate.segment].p_offset, estate.p.phdr64[estate.segment].p_filesz);
|
|
}
|
|
#endif
|
|
/* Check for Etherboot related limitations. Memory
|
|
* between _text and _end is not allowed.
|
|
* Reasons: the Etherboot code/data area.
|
|
*/
|
|
for (estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
|
unsigned long start, mid, end, istart, iend;
|
|
if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
|
|
continue;
|
|
if ((estate.p.phdr64[estate.segment].p_paddr > ULONG_MAX) ||
|
|
((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_filesz) > ULONG_MAX) ||
|
|
((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_memsz) > ULONG_MAX)) {
|
|
printf("ELF segment exceeds address space\n");
|
|
return dead_download;
|
|
}
|
|
start = estate.p.phdr64[estate.segment].p_paddr;
|
|
mid = start + estate.p.phdr64[estate.segment].p_filesz;
|
|
end = start + estate.p.phdr64[estate.segment].p_memsz;
|
|
istart = iend = ULONG_MAX;
|
|
if ((estate.p.phdr64[estate.segment].p_offset < ULONG_MAX) &&
|
|
((estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz) < ULONG_MAX))
|
|
{
|
|
istart = estate.p.phdr64[estate.segment].p_offset;
|
|
iend = istart + estate.p.phdr64[estate.segment].p_filesz;
|
|
}
|
|
if (!prep_segment(start, mid, end, istart, iend)) {
|
|
return dead_download;
|
|
}
|
|
if (!elf_prep_segment(start, mid, end, istart, iend)) {
|
|
return dead_download;
|
|
}
|
|
}
|
|
estate.segment = -1;
|
|
estate.loc = 0;
|
|
estate.skip = 0;
|
|
estate.toread = 0;
|
|
return elf64_download;
|
|
}
|
|
|
|
static sector_t elf64_download(unsigned char *data, unsigned int len, int eof)
|
|
{
|
|
unsigned long skip_sectors = 0;
|
|
unsigned int offset; /* working offset in the current data block */
|
|
int i;
|
|
|
|
offset = 0;
|
|
do {
|
|
if (estate.segment != -1) {
|
|
if (estate.skip) {
|
|
if (estate.skip >= len - offset) {
|
|
estate.skip -= len - offset;
|
|
break;
|
|
}
|
|
offset += estate.skip;
|
|
estate.skip = 0;
|
|
}
|
|
|
|
if (estate.toread) {
|
|
unsigned int cplen;
|
|
cplen = len - offset;
|
|
if (cplen >= estate.toread) {
|
|
cplen = estate.toread;
|
|
}
|
|
memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
|
|
estate.curaddr += cplen;
|
|
estate.toread -= cplen;
|
|
offset += cplen;
|
|
if (estate.toread)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Data left, but current segment finished - look for the next
|
|
* segment (in file offset order) that needs to be loaded.
|
|
* We can only seek forward, so select the program headers,
|
|
* in the correct order.
|
|
*/
|
|
estate.segment = -1;
|
|
for (i = 0; i < estate.e.elf64.e_phnum; i++) {
|
|
if (estate.p.phdr64[i].p_type != PT_LOAD)
|
|
continue;
|
|
if (estate.p.phdr64[i].p_filesz == 0)
|
|
continue;
|
|
if (estate.p.phdr64[i].p_offset < estate.loc + offset)
|
|
continue; /* can't go backwards */
|
|
if ((estate.segment != -1) &&
|
|
(estate.p.phdr64[i].p_offset >= estate.p.phdr64[estate.segment].p_offset))
|
|
continue; /* search minimum file offset */
|
|
estate.segment = i;
|
|
}
|
|
if (estate.segment == -1) {
|
|
/* No more segments to be loaded, so just start the
|
|
* kernel. This saves a lot of network bandwidth if
|
|
* debug info is in the kernel but not loaded. */
|
|
goto elf_startkernel;
|
|
break;
|
|
}
|
|
estate.curaddr = estate.p.phdr64[estate.segment].p_paddr;
|
|
estate.skip = estate.p.phdr64[estate.segment].p_offset - (estate.loc + offset);
|
|
estate.toread = estate.p.phdr64[estate.segment].p_filesz;
|
|
#if ELF_DEBUG
|
|
printf("PHDR %d, size %#lX, curaddr %#lX\n",
|
|
estate.segment, estate.toread, estate.curaddr);
|
|
#endif
|
|
} while (offset < len);
|
|
|
|
estate.loc += len + (estate.skip & ~0x1ff);
|
|
skip_sectors = estate.skip >> 9;
|
|
estate.skip &= 0x1ff;
|
|
|
|
if (eof) {
|
|
unsigned long entry;
|
|
unsigned long machine;
|
|
elf_startkernel:
|
|
entry = estate.e.elf64.e_entry;
|
|
machine = estate.e.elf64.e_machine;
|
|
#if ELF_NOTES
|
|
if (estate.check_ip_checksum) {
|
|
unsigned long bytes = 0;
|
|
uint16_t sum, new_sum;
|
|
|
|
sum = ipchksum(&estate.e.elf64, sizeof(estate.e.elf64));
|
|
bytes = sizeof(estate.e.elf64);
|
|
#if ELF_DEBUG
|
|
printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
|
|
sum, sum, bytes, bytes);
|
|
#endif
|
|
|
|
new_sum = ipchksum(estate.p.phdr64, sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum);
|
|
sum = add_ipchksums(bytes, sum, new_sum);
|
|
bytes += sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum;
|
|
#if ELF_DEBUG
|
|
printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
|
|
new_sum, sum,
|
|
sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum, bytes);
|
|
#endif
|
|
|
|
for(i = 0; i < estate.e.elf64.e_phnum; i++) {
|
|
if (estate.p.phdr64[i].p_type != PT_LOAD)
|
|
continue;
|
|
new_sum = ipchksum(phys_to_virt(estate.p.phdr64[i].p_paddr),
|
|
estate.p.phdr64[i].p_memsz);
|
|
sum = add_ipchksums(bytes, sum, new_sum);
|
|
bytes += estate.p.phdr64[i].p_memsz;
|
|
#if ELF_DEBUG
|
|
printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
|
|
i, new_sum, sum,
|
|
estate.p.phdr64[i].p_memsz, bytes);
|
|
#endif
|
|
|
|
}
|
|
if (estate.ip_checksum != sum) {
|
|
printf("\nImage checksum: %hx != computed checksum: %hx\n",
|
|
estate.ip_checksum, sum);
|
|
longjmp(restart_etherboot, -2);
|
|
}
|
|
}
|
|
#endif
|
|
done(1);
|
|
/* Fixup the offset to the program header so you can find the program headers from
|
|
* the ELF header mknbi needs this.
|
|
*/
|
|
estate.e.elf64.e_phoff = (char *)&estate.p - (char *)&estate.e;
|
|
elf_boot(machine,entry);
|
|
}
|
|
return skip_sectors;
|
|
}
|
|
|
|
#endif /* ELF64_IMAGE */
|