| /* Copyright (c) 2017 Google Inc. | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * ELF loading. */ | 
 |  | 
 | #include <parlib/stdio.h> | 
 | #include <fcntl.h> | 
 | #include <unistd.h> | 
 | #include <libelf.h> | 
 |  | 
 | /* load_elf loads and ELF file. This is almost always a kernel. | 
 |  * We assume that memory is set up correctly, and it will go hard | 
 |  * with you if it is not. The reference parameter records the highest | 
 |  * address we wrote. The initrd can go there.*/ | 
 | uintptr_t load_elf(char *filename, uint64_t offset, uint64_t *highest, | 
 | 		   Elf64_Ehdr *ehdr_out) | 
 | { | 
 | 	Elf64_Ehdr *ehdr; | 
 | 	Elf *elf; | 
 | 	size_t phnum = 0; | 
 | 	Elf64_Phdr *hdrs; | 
 | 	int fd; | 
 | 	uintptr_t ret; | 
 | 	uintptr_t kern_end = 0; | 
 |  | 
 | 	elf_version(EV_CURRENT); | 
 | 	fd = open(filename, O_RDONLY); | 
 | 	if (fd < 0) { | 
 | 		fprintf(stderr, "Can't open %s: %r\n", filename); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	elf = elf_begin(fd, ELF_C_READ, NULL); | 
 | 	if (elf == NULL) { | 
 | 		fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, | 
 | 			filename); | 
 | 		close(fd); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	ehdr = elf64_getehdr(elf); | 
 | 	if (ehdr == NULL) { | 
 | 		fprintf(stderr, "%s: cannot get exec header of %s.\n", | 
 | 		        __func__, filename); | 
 | 		goto fail; | 
 | 	} | 
 |  | 
 | 	if (elf_getphdrnum(elf, &phnum) < 0) { | 
 | 		fprintf(stderr, "%s: cannot get program header num of %s.\n", | 
 | 		        __func__, filename); | 
 | 		goto fail; | 
 | 	} | 
 |  | 
 | 	hdrs = elf64_getphdr(elf); | 
 | 	if (hdrs == NULL) { | 
 | 		fprintf(stderr, "%s: cannot get program headers of %s.\n", | 
 | 		        __func__, filename); | 
 | 		goto fail; | 
 | 	} | 
 |  | 
 | 	for (int i = 0; i < phnum; i++) { | 
 | 		size_t tot; | 
 | 		Elf64_Phdr *h = &hdrs[i]; | 
 | 		uintptr_t pa; | 
 |  | 
 | 		printd("%d: type 0x%lx flags 0x%lx  offset 0x%lx vaddr 0x%lx\npaddr 0x%lx size 0x%lx  memsz 0x%lx align 0x%lx\n", | 
 | 		       i, | 
 | 		       h->p_type,              /* Segment type */ | 
 | 		       h->p_flags,             /* Segment flags */ | 
 | 		       h->p_offset,            /* Segment file offset */ | 
 | 		       h->p_vaddr,             /* Segment virtual address */ | 
 | 		       h->p_paddr,             /* Segment physical address */ | 
 | 		       h->p_filesz,            /* Segment size in file */ | 
 | 		       h->p_memsz,             /* Segment size in memory */ | 
 | 		       h->p_align              /* Segment alignment */); | 
 | 		if (h->p_type != PT_LOAD) | 
 | 			continue; | 
 | 		if ((h->p_flags & (PF_R | PF_W | PF_X)) == 0) | 
 | 			continue; | 
 |  | 
 | 		pa = h->p_paddr; | 
 | 		printd("Read header %d @offset %p to %p (elf PA is %p) %d bytes:", | 
 | 		       i, h->p_offset, pa, h->p_paddr, h->p_filesz); | 
 | 		tot = 0; | 
 | 		while (tot < h->p_filesz) { | 
 | 			int amt = pread(fd, (void *)(pa + tot + offset), | 
 | 					h->p_filesz - tot, h->p_offset + tot); | 
 |  | 
 | 			if (amt < 1) | 
 | 				break; | 
 | 			tot += amt; | 
 | 		} | 
 | 		if (tot < h->p_filesz) { | 
 | 			fprintf(stderr, "%s: got %d bytes, wanted %d bytes\n", | 
 | 			        filename, tot, h->p_filesz); | 
 | 			goto fail; | 
 | 		} | 
 | 		if ((h->p_paddr + h->p_memsz) > kern_end) | 
 | 			kern_end = h->p_paddr + h->p_memsz; | 
 | 	} | 
 |  | 
 | 	close(fd); | 
 | 	ret = ehdr->e_entry + offset; | 
 |  | 
 | 	// Save the values in the header, if the caller wanted them | 
 | 	if (ehdr_out) | 
 | 		*ehdr_out = *ehdr; | 
 |  | 
 | 	elf_end(elf); | 
 | 	if (highest) | 
 | 		*highest = kern_end; | 
 | 	return ret; | 
 | fail: | 
 | 	close(fd); | 
 | 	elf_end(elf); | 
 | 	return 0; | 
 | } |