1 /*
2 * Copyright (c) 2006-2023 Ali Mashtizadeh
3 * All rights reserved.
4 */
5
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <sys/kassert.h>
11 #include <sys/sysctl.h>
12 #include <sys/kmem.h>
13 #include <sys/queue.h>
14 #include <sys/disk.h>
15 #include <sys/elf64.h>
16
17 #include <machine/amd64.h>
18 #include <machine/trap.h>
19 #include <machine/pmap.h>
20 #include <sys/thread.h>
21 #include <sys/spinlock.h>
22 #include <sys/loader.h>
23
24 #include <sys/vfs.h>
25
26 extern Handle *Console_OpenHandle();
27
28 /**
29 * Loader_CheckHeader --
30 *
31 * Check that the program has a valid ELF header.
32 */
33 bool
Loader_CheckHeader(const Elf64_Ehdr * ehdr)34 Loader_CheckHeader(const Elf64_Ehdr *ehdr)
35 {
36 if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
37 ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
38 ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
39 ehdr->e_ident[EI_MAG3] != ELFMAG3)
40 return false;
41
42 if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
43 return false;
44 }
45
46 if (ehdr->e_machine != EM_AMD64) {
47 return false;
48 }
49
50 return true;
51 }
52
53 /**
54 * LoaderLoadSegment --
55 *
56 * Loads a single segment into the target address space. This function loads a
57 * single page at a time because it has to lookup the address mappings through
58 * the page tables.
59 */
60 static void
LoaderLoadSegment(AS * as,VNode * vn,uintptr_t vaddr,uintptr_t offset,uintptr_t len)61 LoaderLoadSegment(AS *as, VNode *vn, uintptr_t vaddr,
62 uintptr_t offset, uintptr_t len)
63 {
64 void *raddr;
65
66 if ((vaddr % PGSIZE) != 0) {
67 uintptr_t maxlen = PGSIZE - (vaddr % PGSIZE);
68 uintptr_t rlen = maxlen < len ? maxlen : len;
69
70 raddr = (void *)DMPA2VA(PMap_Translate(as, vaddr));
71 VFS_Read(vn, raddr, offset, rlen);
72 vaddr += rlen;
73 offset += rlen;
74 len -= rlen;
75 }
76
77 while (len > PGSIZE) {
78 raddr = (void *)DMPA2VA(PMap_Translate(as, vaddr));
79 VFS_Read(vn, raddr, offset, PGSIZE);
80 vaddr += PGSIZE;
81 offset += PGSIZE;
82 len -= PGSIZE;
83 }
84
85 if (len > 0) {
86 raddr = (void *)DMPA2VA(PMap_Translate(as, vaddr));
87 VFS_Read(vn, raddr, offset, len);
88 }
89 }
90
91 /**
92 * LoaderZeroSegment --
93 *
94 * Zeroes a segment of memory in the target address space. This is done one
95 * page a time while translating the virtual address to physical.
96 */
97 static void
LoaderZeroSegment(AS * as,uintptr_t vaddr,uintptr_t len)98 LoaderZeroSegment(AS *as, uintptr_t vaddr, uintptr_t len)
99 {
100 void *raddr;
101
102 if ((vaddr % PGSIZE) != 0) {
103 uintptr_t maxlen = PGSIZE - (vaddr % PGSIZE);
104 uintptr_t rlen = maxlen < len ? maxlen : len;
105
106 raddr = (void *)DMPA2VA(PMap_Translate(as, vaddr));
107 memset(raddr, 0, rlen);
108 vaddr += rlen;
109 len -= rlen;
110 }
111
112 while (len > PGSIZE) {
113 raddr = (void *)DMPA2VA(PMap_Translate(as, vaddr));
114 memset(raddr, 0, PGSIZE);
115 vaddr += PGSIZE;
116 len -= PGSIZE;
117 }
118
119 if (len > 0) {
120 raddr = (void *)DMPA2VA(PMap_Translate(as, vaddr));
121 memset(raddr, 0, len);
122 }
123 }
124
125 /**
126 * Loader_Load --
127 *
128 * Load the ELF binary into the process belonging to the thread.
129 */
130 bool
Loader_Load(Thread * thr,VNode * vn,void * buf,uint64_t len)131 Loader_Load(Thread *thr, VNode *vn, void *buf, uint64_t len)
132 {
133 int i;
134 const Elf64_Ehdr *ehdr;
135 const Elf64_Phdr *phdr;
136 AS *as = thr->space;
137
138 ehdr = (const Elf64_Ehdr *)(buf);
139 phdr = (const Elf64_Phdr *)(buf + ehdr->e_phoff);
140
141 if (!Loader_CheckHeader(ehdr)) {
142 Log(loader, "Not a valid executable!\n");
143 return false;
144 }
145
146 Log(loader, "%8s %16s %8s %8s\n", "Offset", "VAddr", "FileSize", "MemSize");
147 for (i = 0; i < ehdr->e_phnum; i++)
148 {
149 ASSERT(phdr[i].p_type != PT_DYNAMIC);
150 if (phdr[i].p_type == PT_LOAD) {
151 uint64_t va = phdr[i].p_vaddr;
152 uint64_t memsz = phdr[i].p_memsz;
153 Log(loader, "%08llx %016llx %08llx %08llx\n", phdr[i].p_offset,
154 phdr[i].p_vaddr, phdr[i].p_filesz, phdr[i].p_memsz);
155
156 // Make sure it is page aligned
157 va = va & ~(uint64_t)PGMASK;
158 memsz += phdr[i].p_vaddr - va;
159
160 Log(loader, "AllocMap %016llx %08llx\n", va, memsz);
161 if (!PMap_AllocMap(as, va, memsz, PTE_W)) {
162 // XXX: Cleanup!
163 ASSERT(false);
164 return false;
165 }
166 }
167 }
168
169 PMap_AllocMap(as, MEM_USERSPACE_STKBASE, MEM_USERSPACE_STKLEN, PTE_W);
170
171 /* XXXFILLMEIN: Load the ELF segments. */
172
173 /* Save the process entry point (i.e., _start) */
174 thr->proc->entrypoint = ehdr->e_entry;
175
176 return true;
177 }
178
179 /**
180 * Loader_LoadInit --
181 *
182 * The init process is created from the execution kernel thread that
183 * initializes the system. This function initializes the thread and process
184 * state then loads the init binary.
185 */
186 void
Loader_LoadInit()187 Loader_LoadInit()
188 {
189 int status;
190 void *pg;
191 VNode *initvn;
192
193 pg = PAlloc_AllocPage();
194 if (!pg)
195 Panic("Not enough memory!");
196
197 initvn = VFS_Lookup("/sbin/init");
198 status = VFS_Open(initvn);
199 if (status < 0)
200 Panic("Loading init process failed!");
201 status = VFS_Read(initvn, pg, 0, 1024);
202 if (status < 0)
203 Panic("Reading init process failed!");
204
205 Thread *thr = Sched_Current();
206
207 // Open stdin/out/err
208 Handle *handle = Console_OpenHandle();
209 Handle_Add(thr->proc, handle);
210 handle = Console_OpenHandle();
211 Handle_Add(thr->proc, handle);
212 handle = Console_OpenHandle();
213 Handle_Add(thr->proc, handle);
214
215 /*
216 * Load init binary
217 */
218 Loader_Load(thr, initvn, pg, 1024);
219
220 VFS_Close(initvn);
221
222 Log(loader, "Jumping to userspace\n");
223
224 /*
225 * Reload the page tables for the current process
226 */
227 PMap_LoadAS(thr->space); // Reload CR3
228
229 /*
230 * Pass in zero arguments with null pointers to init
231 */
232 uintptr_t ap[3];
233 ap[0] = 0;
234 ap[1] = 0;
235 ap[2] = 0xDEADBEEF;
236 uintptr_t rsp = MEM_USERSPACE_STKTOP - PGSIZE;
237
238 Copy_Out(&ap[0], rsp, sizeof(uintptr_t)*3);
239
240 /*
241 * The last step is to return into userspace handing control to init. We
242 * create a valid trap frame and return into userspace using Trap_Pop().
243 */
244 TrapFrame tf;
245 memset(&tf, 0, sizeof(tf));
246 tf.ds = SEL_UDS | 3;
247 tf.rip = thr->proc->entrypoint;
248 tf.cs = SEL_UCS | 3;
249 tf.rsp = rsp;
250 tf.ss = SEL_UDS | 3;
251 tf.rflags = RFLAGS_IF;
252 tf.rdi = rsp;
253 Trap_Pop(&tf);
254
255 /*
256 * We should never reach this point!
257 */
258 Panic("Unreachable: Trap_Pop() returned!\n");
259 }
260
261
262