The initrd now works.
[akaros.git] / user / vmm / load_elf.c
1 /* Copyright (c) 2017 Google Inc.
2  * See LICENSE for details.
3  *
4  * ELF loading. */
5
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <libelf.h>
10
11 /* load_elf loads and ELF file. This is almost always a kernel.
12  * We assume that memory is set up correctly, and it will go hard
13  * with you if it is not. The reference parameter records the highest
14  * address we wrote. The initrd can go there.*/
15 uintptr_t
16 load_elf(char *filename, uint64_t offset, uint64_t *highest)
17 {
18         Elf64_Ehdr *ehdr;
19         Elf *elf;
20         size_t phnum = 0;
21         Elf64_Phdr *hdrs;
22         int fd;
23         uintptr_t ret;
24         uintptr_t kern_end = 0;
25
26         elf_version(EV_CURRENT);
27         fd = open(filename, O_RDONLY);
28         if (fd < 0) {
29                 fprintf(stderr, "Can't open %s: %r\n", filename);
30                 return 0;
31         }
32
33         elf = elf_begin(fd, ELF_C_READ, NULL);
34         if (elf == NULL) {
35                 fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, filename);
36                 close(fd);
37                 return 0;
38         }
39
40         ehdr = elf64_getehdr(elf);
41         if (ehdr == NULL) {
42                 fprintf(stderr, "%s: cannot get exec header of %s.\n",
43                         __func__, filename);
44                 goto fail;
45         }
46         fprintf(stderr, "%s ELF entry point is %p\n", filename,
47                 (void *)ehdr->e_entry);
48
49         if (elf_getphdrnum(elf, &phnum) < 0) {
50                 fprintf(stderr, "%s: cannot get program header num of %s.\n",
51                         __func__, filename);
52                 goto fail;
53         }
54         fprintf(stderr, "%s has %p program headers\n", filename, phnum);
55
56         hdrs = elf64_getphdr(elf);
57         if (hdrs == NULL) {
58                 fprintf(stderr, "%s: cannot get program headers of %s.\n",
59                         __func__, filename);
60                 goto fail;
61         }
62
63         for (int i = 0; i < phnum; i++) {
64                 size_t tot;
65                 Elf64_Phdr *h = &hdrs[i];
66                 uintptr_t pa;
67
68                 fprintf(stderr,
69                         "%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",
70                         i,
71                         h->p_type,              /* Segment type */
72                         h->p_flags,             /* Segment flags */
73                         h->p_offset,            /* Segment file offset */
74                         h->p_vaddr,             /* Segment virtual address */
75                         h->p_paddr,             /* Segment physical address */
76                         h->p_filesz,            /* Segment size in file */
77                         h->p_memsz,             /* Segment size in memory */
78                         h->p_align              /* Segment alignment */);
79                 if (h->p_type != PT_LOAD)
80                         continue;
81                 if ((h->p_flags & (PF_R | PF_W | PF_X)) == 0)
82                         continue;
83
84                 pa = h->p_paddr;
85                 fprintf(stderr,
86                         "Read header %d @offset %p to %p (elf PA is %p) %d bytes:",
87                         i, h->p_offset, pa, h->p_paddr, h->p_filesz);
88                 tot = 0;
89                 while (tot < h->p_filesz) {
90                         int amt = pread(fd, (void *)(pa + tot + offset), h->p_filesz - tot,
91                                         h->p_offset + tot);
92
93                         if (amt < 1)
94                                 break;
95                         tot += amt;
96                 }
97                 fprintf(stderr, "read a total of %d bytes\n", tot);
98                 if (tot < h->p_filesz) {
99                         fprintf(stderr, "%s: got %d bytes, wanted %d bytes\n",
100                                 filename, tot, h->p_filesz);
101                         goto fail;
102                 }
103                 if ((h->p_paddr + h->p_memsz) > kern_end)
104                         kern_end = h->p_paddr + h->p_memsz;
105         }
106
107         close(fd);
108         ret = ehdr->e_entry + offset;
109         elf_end(elf);
110         if (highest)
111                 *highest = kern_end;
112         return ret;
113 fail:
114         close(fd);
115         elf_end(elf);
116         return 0;
117 }