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.
19 spawn(const char *prog
, const char **argv
)
21 struct Trapframe child_tf
;
29 // Insert your code, following approximately this procedure:
31 // - Open the program file.
32 if ((fd
= open(prog
, O_RDONLY
)) < 0)
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
)) {
41 if (elf
.e_magic
!= ELF_MAGIC
) {
46 // - Use sys_exofork() to create a new environment.
47 if ((child
= sys_exofork()) < 0) {
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) {
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
);
112 cprintf("load elf error: %e", r
);
119 // loop through all the page table entries
120 pn
= UTOP
/ PGSIZE
- 1;
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
),
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)
138 // - Start the child process running with sys_env_set_status().
139 if ((r
= sys_env_set_status(child
, ENV_RUNNABLE
)) < 0)
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.
161 init_stack(envid_t child
, const char **argv
, uintptr_t *init_esp
)
165 uintptr_t *argv_store
;
168 // Count the number of arguments (argc)
169 // and the total amount of space needed for strings (string_size).
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
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
)
189 // Allocate the single stack page at UTEMP.
190 if ((r
= sys_page_alloc(0, (void*) UTEMP
, PTE_P
|PTE_U
|PTE_W
)) < 0)
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
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
;
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
);
245 if ((r
= sys_page_unmap(0, UTEMP
)) < 0)
251 static int load_elf_to_child(int fd
, struct Proghdr
*ph
, envid_t child
)
253 uint32_t offset
, cur_va
;
257 if (ph
->p_flags
& ELF_PROG_FLAG_WRITE
) {
258 // read/write data here
259 offset
= ph
->p_offset
;
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,
271 PTE_P
|PTE_U
|PTE_W
)) < 0)
273 if ((r
= seek(fd
, ROUNDDOWN(offset
, PGSIZE
))) < 0)
276 (void *)UTEMP
+ (offset
& 0xfff),
279 if ((r
= sys_page_map(0,
282 (void *)ROUNDDOWN(cur_va
, PGSIZE
),
283 PTE_P
|PTE_U
|PTE_W
)) < 0)
285 if ((r
= sys_page_unmap(0, (void *)UTEMP
)) < 0)
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,
301 PTE_P
|PTE_U
|PTE_W
)) < 0)
305 memset((void *)UTEMP
+ (offset
& 0xfff), 0, size
);
307 if ((r
= sys_page_map(0,
310 (void *)ROUNDDOWN(cur_va
, PGSIZE
),
311 PTE_P
|PTE_U
|PTE_W
)) < 0)
313 if ((r
= sys_page_unmap(0, (void *)UTEMP
)) < 0)
320 // text and read-only data
321 offset
= ph
->p_offset
;
323 while (cur_va
< ph
->p_va
+ ph
->p_filesz
) {
324 size
= PGSIZE
- (offset
& 0xfff);
326 if ((r
= read_map(fd
, offset
, &blk
)) < 0)
328 if ((r
= sys_page_map(0,
329 ROUNDDOWN(blk
, PGSIZE
),
331 (void *)ROUNDDOWN(cur_va
, PGSIZE
),