1 /* See COPYRIGHT for copyright information. */
5 #include <inc/string.h>
6 #include <inc/assert.h>
10 #include <kern/trap.h>
11 #include <kern/syscall.h>
12 #include <kern/console.h>
13 #include <kern/sched.h>
15 // Print a string to the system console.
16 // The string is exactly 'len' characters long.
17 // Destroys the environment on memory errors.
19 sys_cputs(const char *s
, size_t len
)
21 // Check that the user has permission to read memory [s, s+len).
22 // Destroy the environment if not.
23 // LAB 3: Your code here.
24 user_mem_assert(curenv
, (void *)s
, len
, 0);
26 // Print the string supplied by the user.
27 cprintf("%.*s", len
, s
);
30 // Read a character from the system console.
31 // Returns the character.
37 // The cons_getc() primitive doesn't wait for a character,
38 // but the sys_cgetc() system call does.
39 while ((c
= cons_getc()) == 0)
45 // Returns the current environment's envid.
49 return curenv
->env_id
;
52 // Destroy a given environment (possibly the currently running environment).
54 // Returns 0 on success, < 0 on error. Errors are:
55 // -E_BAD_ENV if environment envid doesn't currently exist,
56 // or the caller doesn't have permission to change envid.
58 sys_env_destroy(envid_t envid
)
63 if ((r
= envid2env(envid
, &e
, 1)) < 0)
69 // Deschedule current environment and pick a different one to run.
76 // Allocate a new environment.
77 // Returns envid of new environment, or < 0 on error. Errors are:
78 // -E_NO_FREE_ENV if no free environment is available.
82 // Create the new environment with env_alloc(), from kern/env.c.
83 // It should be left as env_alloc created it, except that
84 // status is set to ENV_NOT_RUNNABLE, and the register set is copied
85 // from the current environment -- but tweaked so sys_exofork
86 // will appear to return 0.
87 // LAB 4: Your code here.
90 if (env_alloc(&child
, curenv
->env_id
) < 0)
91 return -E_NO_FREE_ENV
;
93 child
->env_status
= ENV_NOT_RUNNABLE
;
94 child
->env_tf
= curenv
->env_tf
;
95 // install the pgfault upcall to the child
96 child
->env_pgfault_upcall
= curenv
->env_pgfault_upcall
;
97 // tweak the register eax of the child,
98 // thus, the child will look like the return value
99 // of the the system call is zero.
100 child
->env_tf
.tf_regs
.reg_eax
= 0;
101 // but notice that the return value of the parent
102 // is the env id of the child
103 return child
->env_id
;
106 // Set envid's env_status to status, which must be ENV_RUNNABLE
107 // or ENV_NOT_RUNNABLE.
109 // Returns 0 on success, < 0 on error. Errors are:
110 // -E_BAD_ENV if environment envid doesn't currently exist,
111 // or the caller doesn't have permission to change envid.
112 // -E_INVAL if status is not a valid status for an environment.
114 sys_env_set_status(envid_t envid
, int status
)
116 // Hint: Use the 'envid2env' function from kern/env.c to translate an
117 // envid to a struct Env.
118 // You should set envid2env's third argument to 1, which will
119 // check whether the current environment has permission to set
121 // LAB 4: Your code here.
125 if ((r
= envid2env(envid
, &task
, 1)) < 0)
128 if (status
!= ENV_FREE
&&
129 status
!= ENV_RUNNABLE
&&
130 status
!= ENV_NOT_RUNNABLE
)
133 task
->env_status
= status
;
138 // Set envid's trap frame to 'tf'.
139 // tf is modified to make sure that user environments always run at code
140 // protection level 3 (CPL 3) with interrupts enabled.
142 // Returns 0 on success, < 0 on error. Errors are:
143 // -E_BAD_ENV if environment envid doesn't currently exist,
144 // or the caller doesn't have permission to change envid.
146 sys_env_set_trapframe(envid_t envid
, struct Trapframe
*tf
)
148 // LAB 4: Your code here.
149 // Remember to check whether the user has supplied us with a good
154 if ((r
= envid2env(envid
, &task
, 1)) < 0)
162 // Set the page fault upcall for 'envid' by modifying the corresponding struct
163 // Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the
164 // kernel will push a fault record onto the exception stack, then branch to
167 // Returns 0 on success, < 0 on error. Errors are:
168 // -E_BAD_ENV if environment envid doesn't currently exist,
169 // or the caller doesn't have permission to change envid.
171 sys_env_set_pgfault_upcall(envid_t envid
, void *func
)
173 // LAB 4: Your code here.
176 if (envid2env(envid
, &task
, 1) < 0)
179 task
->env_pgfault_upcall
= func
;
184 // Allocate a page of memory and map it at 'va' with permission
185 // 'perm' in the address space of 'envid'.
186 // The page's contents are set to 0.
187 // If a page is already mapped at 'va', that page is unmapped as a
190 // perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
191 // but no other bits may be set.
193 // Return 0 on success, < 0 on error. Errors are:
194 // -E_BAD_ENV if environment envid doesn't currently exist,
195 // or the caller doesn't have permission to change envid.
196 // -E_INVAL if va >= UTOP, or va is not page-aligned.
197 // -E_INVAL if perm is inappropriate (see above).
198 // -E_NO_MEM if there's no memory to allocate the new page,
199 // or to allocate any necessary page tables.
201 sys_page_alloc(envid_t envid
, void *va
, int perm
)
203 // Hint: This function is a wrapper around page_alloc() and
204 // page_insert() from kern/pmap.c.
205 // Most of the new code you write should be to check the
206 // parameters for correctness.
207 // If page_insert() fails, remember to free the page you
209 // LAB 4: Your code here.
213 //cprintf("sys_page_alloc: [%08x] .\n", envid);
214 if (envid2env(envid
, &task
, 1) < 0)
217 if (page_alloc(&page
) < 0)
220 if ((unsigned int)va
>= UTOP
|| va
!= ROUNDDOWN(va
, PGSIZE
))
223 // PTE_U and PTE_P must be set
224 if (!(perm
& PTE_U
) || !(perm
& PTE_P
))
226 // other bits than PTE_{U,P,W,AVAIL} are set
227 if (perm
& ((~(PTE_U
| PTE_P
| PTE_W
| PTE_AVAIL
)) & 0xfff))
230 memset(page2kva(page
), 0, PGSIZE
);
231 if (page_insert(task
->env_pgdir
, page
, va
, perm
) < 0) {
236 //cprintf("allocated page: [%08x].\n", page2pa(page));
240 // Map the page of memory at 'srcva' in srcenvid's address space
241 // at 'dstva' in dstenvid's address space with permission 'perm'.
242 // Perm has the same restrictions as in sys_page_alloc, except
243 // that it also must not grant write access to a read-only
246 // Return 0 on success, < 0 on error. Errors are:
247 // -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
248 // or the caller doesn't have permission to change one of them.
249 // -E_INVAL if srcva >= UTOP or srcva is not page-aligned,
250 // or dstva >= UTOP or dstva is not page-aligned.
251 // -E_INVAL is srcva is not mapped in srcenvid's address space.
252 // -E_INVAL if perm is inappropriate (see sys_page_alloc).
253 // -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
255 // -E_NO_MEM if there's no memory to allocate the new page,
256 // or to allocate any necessary page tables.
258 sys_page_map(envid_t srcenvid
, void *srcva
,
259 envid_t dstenvid
, void *dstva
, int perm
)
261 // Hint: This function is a wrapper around page_lookup() and
262 // page_insert() from kern/pmap.c.
263 // Again, most of the new code you write should be to check the
264 // parameters for correctness.
265 // Use the third argument to page_lookup() to
266 // check the current permissions on the page.
267 // LAB 4: Your code here.
268 struct Env
*srcenv
, *dstenv
;
270 pte_t
*srcpte
, *dstpte
;
272 if (envid2env(srcenvid
, &srcenv
, 1) < 0 ||
273 envid2env(dstenvid
, &dstenv
, 1) < 0)
276 if ((unsigned int)srcva
>= UTOP
|| srcva
!= ROUNDDOWN(srcva
, PGSIZE
) ||
277 (unsigned int)dstva
>= UTOP
|| dstva
!= ROUNDDOWN(dstva
, PGSIZE
))
280 if ((page
= page_lookup(srcenv
->env_pgdir
, srcva
, &srcpte
)) == NULL
)
283 // PTE_U and PTE_P must be set
284 if (!(perm
& PTE_U
) || !(perm
& PTE_P
))
286 // other bits than PTE_{U,P,W,AVAIL} are set
287 if (perm
& ((~(PTE_U
| PTE_P
| PTE_W
| PTE_AVAIL
)) & 0xfff))
289 // perm has PTE_W, but scrpte is read-only.
290 if ((perm
& PTE_W
) && !(*srcpte
& PTE_W
))
293 if (page_insert(dstenv
->env_pgdir
, page
, dstva
, perm
) < 0)
295 /*cprintf("map [%08x] %08x(%08x) -> [%08x] %08x(%08x) perm: %x\n",
296 srcenv->env_id, srcva, *srcpte,
297 dstenv->env_id, dstva, *dstpte, perm);*/
302 // Unmap the page of memory at 'va' in the address space of 'envid'.
303 // If no page is mapped, the function silently succeeds.
305 // Return 0 on success, < 0 on error. Errors are:
306 // -E_BAD_ENV if environment envid doesn't currently exist,
307 // or the caller doesn't have permission to change envid.
308 // -E_INVAL if va >= UTOP, or va is not page-aligned.
310 sys_page_unmap(envid_t envid
, void *va
)
312 // Hint: This function is a wrapper around page_remove().
313 // LAB 4: Your code here.
316 if (envid2env(envid
, &task
, 1) < 0)
319 if ((unsigned int)va
>= UTOP
|| va
!= ROUNDDOWN(va
, PGSIZE
))
322 page_remove(task
->env_pgdir
, va
);
327 // Try to send 'value' to the target env 'envid'.
328 // If va != 0, then also send page currently mapped at 'va',
329 // so that receiver gets a duplicate mapping of the same page.
331 // The send fails with a return value of -E_IPC_NOT_RECV if the
332 // target has not requested IPC with sys_ipc_recv.
334 // Otherwise, the send succeeds, and the target's ipc fields are
335 // updated as follows:
336 // env_ipc_recving is set to 0 to block future sends;
337 // env_ipc_from is set to the sending envid;
338 // env_ipc_value is set to the 'value' parameter;
339 // env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise.
340 // The target environment is marked runnable again, returning 0
341 // from the paused ipc_recv system call.
343 // If the sender sends a page but the receiver isn't asking for one,
344 // then no page mapping is transferred, but no error occurs.
345 // The ipc doesn't happen unless no errors occur.
347 // Returns 0 on success where no page mapping occurs,
348 // 1 on success where a page mapping occurs, and < 0 on error.
350 // -E_BAD_ENV if environment envid doesn't currently exist.
351 // (No need to check permissions.)
352 // -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
353 // or another environment managed to send first.
354 // -E_INVAL if srcva < UTOP but srcva is not page-aligned.
355 // -E_INVAL if srcva < UTOP and perm is inappropriate
356 // (see sys_page_alloc).
357 // -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's
359 // -E_NO_MEM if there's not enough memory to map srcva in envid's
362 sys_ipc_try_send(envid_t envid
, uint32_t value
, void *srcva
, unsigned perm
)
364 // LAB 4: Your code here.
370 if ((r
= envid2env(envid
, &target
, 0)) < 0)
373 if (!target
->env_ipc_recving
)
374 return -E_IPC_NOT_RECV
;
376 // srcva is not null, then
377 // we need to map it, thus sharing the map
379 if ((unsigned int)srcva
>= UTOP
)
382 if (srcva
!= ROUNDDOWN(srcva
, PGSIZE
))
385 if ((page
= page_lookup(curenv
->env_pgdir
, srcva
, &pte
)) == NULL
)
388 // PTE_U and PTE_P must be set
389 if (!(perm
& PTE_U
) || !(perm
& PTE_P
))
391 // other bits than PTE_{U,P,W,AVAIL} are set
392 if (perm
& ((~(PTE_U
| PTE_P
| PTE_W
| PTE_AVAIL
)) & 0xfff))
394 // perm has PTE_W, but scrpte is read-only.
395 if ((perm
& PTE_W
) && !(*pte
& PTE_W
))
398 if (page_insert(target
->env_pgdir
, page
, target
->env_ipc_dstva
, perm
) < 0)
404 target
->env_ipc_recving
= 0;
405 target
->env_ipc_value
= value
;
406 target
->env_ipc_from
= curenv
->env_id
;
408 target
->env_ipc_perm
= perm
;
410 target
->env_ipc_perm
= 0;
411 target
->env_status
= ENV_RUNNABLE
;
416 // Block until a value is ready. Record that you want to receive
417 // using the env_ipc_recving and env_ipc_dstva fields of struct Env,
418 // mark yourself not runnable, and then give up the CPU.
420 // If 'dstva' is < UTOP, then you are willing to receive a page of data.
421 // 'dstva' is the virtual address at which the sent page should be mapped.
423 // This function only returns on error, but the system call will eventually
424 // return 0 on success.
425 // Return < 0 on error. Errors are:
426 // -E_INVAL if dstva < UTOP but dstva is not page-aligned.
428 sys_ipc_recv(void *dstva
)
430 // LAB 4: Your code here.
431 if ((unsigned int)dstva
>= UTOP
|| dstva
!= ROUNDDOWN(dstva
, PGSIZE
))
434 curenv
->env_ipc_dstva
= dstva
;
435 curenv
->env_ipc_recving
= 1;
436 curenv
->env_status
= ENV_NOT_RUNNABLE
;
437 // set the return value to be zero,
438 // it is necessary, because the 'return' statement
439 // after 'sched_yield' will never be executed,
440 // actually it is skipped.
441 curenv
->env_tf
.tf_regs
.reg_eax
= 0;
448 sys_phy_page(envid_t envid
, void *va
)
454 if (envid2env(envid
, &task
, 1) < 0)
457 page
= page_lookup(task
->env_pgdir
, va
, &pte
);
464 // Dispatches to the correct kernel function, passing the arguments.
466 syscall(uint32_t syscallno
, uint32_t a1
, uint32_t a2
, uint32_t a3
, uint32_t a4
, uint32_t a5
)
468 // Call the function corresponding to the 'syscallno' parameter.
469 // Return any appropriate return value.
470 // LAB 3: Your code here.
475 sys_cputs((const char *)a1
, (size_t)a2
);
481 ret
= sys_getenvid();
483 case SYS_env_destroy
:
484 ret
= sys_env_destroy((envid_t
)a1
);
487 ret
= sys_page_alloc((envid_t
)a1
, (void *)a2
, (int)a3
);
490 ret
= sys_page_map((envid_t
)a1
, (void *)a2
,
491 (envid_t
)a3
, (void *)a4
, (int)a5
);
494 ret
= sys_page_unmap((envid_t
)a1
, (void *)a2
);
499 case SYS_env_set_status
:
500 ret
= sys_env_set_status((envid_t
)a1
, (int)a2
);
502 case SYS_env_set_trapframe
:
503 ret
= sys_env_set_trapframe((envid_t
)a1
, (struct Trapframe
*)a2
);
505 case SYS_env_set_pgfault_upcall
:
506 ret
= sys_env_set_pgfault_upcall((envid_t
)a1
, (void *)a2
);
512 ret
= sys_phy_page((envid_t
)a1
, (void *)a2
);
514 case SYS_ipc_try_send
:
515 ret
= sys_ipc_try_send((envid_t
)a1
, (uint32_t)a2
,
516 (void *)a3
, (int)a4
);
519 ret
= sys_ipc_recv((void *)a1
);