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