fix O_RDWR bug.
[mit-jos.git] / lib / spawn.c
blob291a4940842147424a53ec0015f90870dffba3a4
1 #include <inc/lib.h>
2 #include <inc/elf.h>
4 #define UTEMP2USTACK(addr) ((void*) (addr) + (USTACKTOP - PGSIZE) - UTEMP)
5 #define UTEMP2 (UTEMP + PGSIZE)
6 #define UTEMP3 (UTEMP2 + PGSIZE)
8 // Helper functions for spawn.
9 static int init_stack(envid_t child, const char **argv, uintptr_t *init_esp);
10 static int copy_shared_pages(envid_t child);
11 static int load_elf_to_child(int fd, struct Proghdr *ph, envid_t child);
13 // Spawn a child process from a program image loaded from the file system.
14 // prog: the pathname of the program to run.
15 // argv: pointer to null-terminated array of pointers to strings,
16 // which will be passed to the child as its command-line arguments.
17 // Returns child envid on success, < 0 on failure.
18 int
19 spawn(const char *prog, const char **argv)
21 struct Trapframe child_tf;
22 envid_t child;
23 struct Elf elf;
24 struct Proghdr ph;
25 int r, i, pn;
26 int fd;
27 uintptr_t esp;
29 // Insert your code, following approximately this procedure:
31 // - Open the program file.
32 if ((fd = open(prog, O_RDONLY)) < 0)
33 return fd;
35 // - Read the ELF header, as you have before, and sanity check its
36 // magic number. (Check out your load_icode!)
37 if (read(fd, (void *)&elf, sizeof(elf)) != sizeof(elf)) {
38 close(fd);
39 return -E_INVAL;
41 if (elf.e_magic != ELF_MAGIC) {
42 close(fd);
43 return -E_INVAL;
46 // - Use sys_exofork() to create a new environment.
47 if ((child = sys_exofork()) < 0) {
48 close(fd);
49 return child;
52 // - Set child_tf to an initial struct Trapframe for the child.
53 // Hint: The sys_exofork() system call has already created
54 // a good basis, in envs[ENVX(child)].env_tf.
55 // Hint: You must do something with the program's entry point.
56 // What? (See load_icode!)
58 // - Call the init_stack() function above to set up
59 // the initial stack page for the child environment.
60 if ((r = init_stack(child, argv, &esp)) < 0) {
61 close(fd);
62 return r;
64 child_tf = envs[ENVX(child)].env_tf;
65 child_tf.tf_eip = elf.e_entry;
66 child_tf.tf_esp = esp;
68 // - Map all of the program's segments that are of p_type
69 // ELF_PROG_LOAD into the new environment's address space.
70 // Use the p_flags field in the Proghdr for each segment
71 // to determine how to map the segment:
73 // * If the ELF flags do not include ELF_PROG_FLAG_WRITE,
74 // then the segment contains text and read-only data.
75 // Use read_map() to read the contents of this segment,
76 // and map the pages it returns directly into the child
77 // so that multiple instances of the same program
78 // will share the same copy of the program text.
79 // Be sure to map the program text read-only in the child.
80 // Read_map is like read but returns a pointer to the data in
81 // *blk rather than copying the data into another buffer.
83 // * If the ELF segment flags DO include ELF_PROG_FLAG_WRITE,
84 // then the segment contains read/write data and bss.
85 // As with load_icode() in Lab 3, such an ELF segment
86 // occupies p_memsz bytes in memory, but only the FIRST
87 // p_filesz bytes of the segment are actually loaded
88 // from the executable file - you must clear the rest to zero.
89 // For each page to be mapped for a read/write segment,
90 // allocate a page in the parent temporarily at UTEMP,
91 // read() the appropriate portion of the file into that page
92 // and/or use memset() to zero non-loaded portions.
93 // (You can avoid calling memset(), if you like, if
94 // page_alloc() returns zeroed pages already.)
95 // Then insert the page mapping into the child.
96 // Look at init_stack() for inspiration.
97 // Be sure you understand why you can't use read_map() here.
99 // Note: None of the segment addresses or lengths above
100 // are guaranteed to be page-aligned, so you must deal with
101 // these non-page-aligned values appropriately.
102 // The ELF linker does, however, guarantee that no two segments
103 // will overlap on the same page; and it guarantees that
104 // PGOFF(ph->p_offset) == PGOFF(ph->p_va).
105 for (i = 0; i < elf.e_phnum; i++) {
106 seek(fd, elf.e_phoff + sizeof(struct Proghdr) * i);
107 read(fd, &ph, sizeof(struct Proghdr));
109 if (ph.p_type == ELF_PROG_LOAD) {
110 r = load_elf_to_child(fd, &ph, child);
111 if (r < 0) {
112 cprintf("load elf error: %e", r);
113 close(fd);
114 return r;
118 close(fd);
119 // loop through all the page table entries
120 pn = UTOP / PGSIZE - 1;
121 while (--pn >= 0)
122 if (!(vpd[pn >> 10] & PTE_P))
123 pn = (pn >> 10) << 10;
124 else if ((vpt[pn] & PTE_P) && (vpt[pn] & PTE_SHARE)) {
125 // propagate the PTE_SHARE pages
126 r = sys_page_map(0, (void *)(pn*PGSIZE),
127 child, (void *)(pn*PGSIZE),
128 vpt[pn] & PTE_USER);
129 if (r < 0)
130 return r;
133 // - Call sys_env_set_trapframe(child, &child_tf) to set up the
134 // correct initial eip and esp values in the child.
135 if ((r = sys_env_set_trapframe(child, &child_tf)) < 0)
136 return r;
138 // - Start the child process running with sys_env_set_status().
139 if ((r = sys_env_set_status(child, ENV_RUNNABLE)) < 0)
140 return r;
142 return child;
145 // Spawn, taking command-line arguments array directly on the stack.
147 spawnl(const char *prog, const char *arg0, ...)
149 return spawn(prog, &arg0);
153 // Set up the initial stack page for the new child process with envid 'child'
154 // using the arguments array pointed to by 'argv',
155 // which is a null-terminated array of pointers to null-terminated strings.
157 // On success, returns 0 and sets *init_esp
158 // to the initial stack pointer with which the child should start.
159 // Returns < 0 on failure.
160 static int
161 init_stack(envid_t child, const char **argv, uintptr_t *init_esp)
163 size_t string_size;
164 char *string_store;
165 uintptr_t *argv_store;
166 int argc, i, r, len;
168 // Count the number of arguments (argc)
169 // and the total amount of space needed for strings (string_size).
170 string_size = 0;
171 for (argc = 0; argv[argc] != 0; argc++)
172 string_size += strlen(argv[argc]) + 1;
174 // Determine where to place the strings and the argv array.
175 // Set up pointers into the temporary page 'UTEMP'; we'll map a page
176 // there later, then remap that page into the child environment
177 // at (USTACKTOP - PGSIZE).
178 // strings is the topmost thing on the stack.
179 string_store = (char*) UTEMP + PGSIZE - string_size;
180 // argv is below that. There's one argument pointer per argument, plus
181 // a null pointer.
182 argv_store = (uintptr_t*) (ROUNDDOWN(string_store, 4) - 4 * (argc + 1));
184 // Make sure that argv, strings, and the 2 words that hold 'argc'
185 // and 'argv' themselves will all fit in a single stack page.
186 if ((void*) (argv_store - 2) < (void*) UTEMP)
187 return -E_NO_MEM;
189 // Allocate the single stack page at UTEMP.
190 if ((r = sys_page_alloc(0, (void*) UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
191 return r;
193 // Replace this with your code to:
195 // * Initialize 'argv_store[i]' to point to argument string i,
196 // for all 0 <= i < argc.
197 // Also, copy the argument strings from 'argv' into the
198 // newly-allocated stack page.
199 // Hint: Copy the argument strings into string_store.
200 // Hint: Make sure that argv_store uses addresses valid in the
201 // CHILD'S environment! The string_store variable itself
202 // points into page UTEMP, but the child environment will have
203 // this page mapped at USTACKTOP - PGSIZE. Check out the
204 // UTEMP2USTACK macro defined above.
206 // * Set 'argv_store[argc]' to 0 to null-terminate the args array.
208 // * Push two more words onto the child's stack below 'args',
209 // containing the argc and argv parameters to be passed
210 // to the child's umain() function.
211 // argv should be below argc on the stack.
212 // (Again, argv should use an address valid in the child's
213 // environment.)
215 // * Set *init_esp to the initial stack pointer for the child,
216 // (Again, use an address valid in the child's environment.)
218 for (i = 0; i < argc; i++) {
219 len = strlen(argv[i]);
220 // copy the arguments to string_store
221 memmove(string_store, argv[i], len);
222 // make it null-terminated
223 string_store[len] = 0;
224 // make the right reference
225 argv_store[i] = UTEMP2USTACK(string_store);
226 // advance the string_store pointer
227 string_store += len + 1;
229 argv_store[argc] = 0;
230 // push 'argv' ptr onto the stack
231 argv_store[-1] = (uintptr_t)UTEMP2USTACK(argv_store);
232 // push 'argc' onto the stack
233 argv_store[-2] = argc;
234 argv_store -= 2;
235 // return the right esp value to the child process
236 *init_esp = UTEMP2USTACK(argv_store);
237 // After completing the stack, map it into the child's address space
238 // and unmap it from ours!
239 if ((r = sys_page_map(0, UTEMP,
240 child, (void*) (USTACKTOP - PGSIZE),
241 PTE_P | PTE_U | PTE_W)) < 0) {
242 sys_page_unmap(0, UTEMP);
243 return r;
245 if ((r = sys_page_unmap(0, UTEMP)) < 0)
246 return r;
248 return 0;
251 static int load_elf_to_child(int fd, struct Proghdr *ph, envid_t child)
253 uint32_t offset, cur_va;
254 int r, size;
255 void *blk;
257 if (ph->p_flags & ELF_PROG_FLAG_WRITE) {
258 // read/write data here
259 offset = ph->p_offset;
260 cur_va = ph->p_va;
262 while (cur_va < ph->p_va + ph->p_filesz) {
263 // calculate 'size', if it exceeds p_filesz,
264 // then, adjust the 'size'.
265 size = PGSIZE - (offset & 0xfff);
266 if (cur_va + size >= ph->p_va + ph->p_filesz)
267 size = ph->p_va + ph->p_filesz - cur_va;
269 if ((r = sys_page_alloc(0,
270 (void *)UTEMP,
271 PTE_P |PTE_U |PTE_W)) < 0)
272 return r;
273 if ((r = seek(fd, ROUNDDOWN(offset, PGSIZE))) < 0)
274 return r;
275 if ((r = read(fd,
276 (void *)UTEMP + (offset & 0xfff),
277 size)) < 0)
278 return r;
279 if ((r = sys_page_map(0,
280 (void *)UTEMP,
281 child,
282 (void *)ROUNDDOWN(cur_va, PGSIZE),
283 PTE_P |PTE_U |PTE_W)) < 0)
284 return r;
285 if ((r = sys_page_unmap(0, (void *)UTEMP)) < 0)
286 return r;
288 offset += size;
289 cur_va += size;
291 // zero the region from ph->p_filesz to ph->p_memsz
292 cur_va = ROUNDUP(cur_va, PGSIZE);
293 offset = ROUNDUP(offset, PGSIZE);
294 while (cur_va < ph->p_va + ph->p_memsz) {
295 size = PGSIZE - (offset & 0xfff);
296 if (cur_va + size >= ph->p_va + ph->p_memsz)
297 size = ph->p_va + ph->p_memsz - cur_va;
299 if ((r = sys_page_alloc(0,
300 (void *)UTEMP,
301 PTE_P |PTE_U |PTE_W)) < 0)
302 return r;
304 // zero them
305 memset((void *)UTEMP + (offset & 0xfff), 0, size);
307 if ((r = sys_page_map(0,
308 (void *)UTEMP,
309 child,
310 (void *)ROUNDDOWN(cur_va, PGSIZE),
311 PTE_P |PTE_U |PTE_W)) < 0)
312 return r;
313 if ((r = sys_page_unmap(0, (void *)UTEMP)) < 0)
314 return r;
316 offset += size;
317 cur_va += size;
319 } else {
320 // text and read-only data
321 offset = ph->p_offset;
322 cur_va = ph->p_va;
323 while (cur_va < ph->p_va + ph->p_filesz) {
324 size = PGSIZE - (offset & 0xfff);
326 if ((r = read_map(fd, offset, &blk)) < 0)
327 return r;
328 if ((r = sys_page_map(0,
329 ROUNDDOWN(blk, PGSIZE),
330 child,
331 (void *)ROUNDDOWN(cur_va, PGSIZE),
332 PTE_P |PTE_U)) < 0)
333 return r;
335 offset += size;
336 cur_va += size;
339 return 0;