still serv_close bug...
[mit-jos.git] / kern / syscall.c
blobda2498af6ad798495ec857aac93c89d59878f127
1 /* See COPYRIGHT for copyright information. */
3 #include <inc/x86.h>
4 #include <inc/error.h>
5 #include <inc/string.h>
6 #include <inc/assert.h>
8 #include <kern/env.h>
9 #include <kern/pmap.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.
18 static void
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.
32 static int
33 sys_cgetc(void)
35 int c;
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)
40 /* do nothing */;
42 return c;
45 // Returns the current environment's envid.
46 static envid_t
47 sys_getenvid(void)
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.
57 static int
58 sys_env_destroy(envid_t envid)
60 int r;
61 struct Env *e;
63 if ((r = envid2env(envid, &e, 1)) < 0)
64 return r;
65 env_destroy(e);
66 return 0;
69 // Deschedule current environment and pick a different one to run.
70 static void
71 sys_yield(void)
73 sched_yield();
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.
79 static envid_t
80 sys_exofork(void)
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.
88 struct Env *child;
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.
113 static int
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
120 // envid's status.
121 // LAB 4: Your code here.
122 int r;
123 struct Env *task;
125 if ((r = envid2env(envid, &task, 1)) < 0)
126 return -E_BAD_ENV;
128 if (status != ENV_FREE &&
129 status != ENV_RUNNABLE &&
130 status != ENV_NOT_RUNNABLE)
131 return -E_INVAL;
133 task->env_status = status;
135 return 0;
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.
145 static int
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
150 // address!
151 int r;
152 struct Env *task;
154 if ((r = envid2env(envid, &task, 1)) < 0)
155 return -E_BAD_ENV;
157 task->env_tf = *tf;
159 return 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
165 // 'func'.
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.
170 static int
171 sys_env_set_pgfault_upcall(envid_t envid, void *func)
173 // LAB 4: Your code here.
174 struct Env *task;
176 if (envid2env(envid, &task, 1) < 0)
177 return -E_BAD_ENV;
179 task->env_pgfault_upcall = func;
181 return 0;
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
188 // side effect.
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.
200 static int
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
208 // allocated!
209 // LAB 4: Your code here.
210 struct Env *task;
211 struct Page *page;
213 //cprintf("sys_page_alloc: [%08x] .\n", envid);
214 if (envid2env(envid, &task, 1) < 0)
215 return -E_BAD_ENV;
217 if (page_alloc(&page) < 0)
218 return -E_NO_MEM;
220 if ((unsigned int)va >= UTOP || va != ROUNDDOWN(va, PGSIZE))
221 return -E_INVAL;
223 // PTE_U and PTE_P must be set
224 if (!(perm & PTE_U) || !(perm & PTE_P))
225 return -E_INVAL;
226 // other bits than PTE_{U,P,W,AVAIL} are set
227 if (perm & ((~(PTE_U | PTE_P | PTE_W | PTE_AVAIL)) & 0xfff))
228 return -E_INVAL;
230 memset(page2kva(page), 0, PGSIZE);
231 if (page_insert(task->env_pgdir, page, va, perm) < 0) {
232 page_free(page);
233 return -E_NO_MEM;
236 //cprintf("allocated page: [%08x].\n", page2pa(page));
237 return 0;
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
244 // page.
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
254 // address space.
255 // -E_NO_MEM if there's no memory to allocate the new page,
256 // or to allocate any necessary page tables.
257 static int
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;
269 struct Page *page;
270 pte_t *srcpte, *dstpte;
272 if (envid2env(srcenvid, &srcenv, 1) < 0 ||
273 envid2env(dstenvid, &dstenv, 1) < 0)
274 return -E_BAD_ENV;
276 if ((unsigned int)srcva >= UTOP || srcva != ROUNDDOWN(srcva, PGSIZE) ||
277 (unsigned int)dstva >= UTOP || dstva != ROUNDDOWN(dstva, PGSIZE))
278 return -E_INVAL;
280 if ((page = page_lookup(srcenv->env_pgdir, srcva, &srcpte)) == NULL)
281 return -E_INVAL;
283 // PTE_U and PTE_P must be set
284 if (!(perm & PTE_U) || !(perm & PTE_P))
285 return -E_INVAL;
286 // other bits than PTE_{U,P,W,AVAIL} are set
287 if (perm & ((~(PTE_U | PTE_P | PTE_W | PTE_AVAIL)) & 0xfff))
288 return -E_INVAL;
289 // perm has PTE_W, but scrpte is read-only.
290 if ((perm & PTE_W) && !(*srcpte & PTE_W))
291 return -E_INVAL;
293 if (page_insert(dstenv->env_pgdir, page, dstva, perm) < 0)
294 return -E_NO_MEM;
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);*/
299 return 0;
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.
309 static int
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.
314 struct Env *task;
316 if (envid2env(envid, &task, 1) < 0)
317 return -E_BAD_ENV;
319 if ((unsigned int)va >= UTOP || va != ROUNDDOWN(va, PGSIZE))
320 return -E_INVAL;
322 page_remove(task->env_pgdir, va);
324 return 0;
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.
349 // Errors are:
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
358 // address space.
359 // -E_NO_MEM if there's not enough memory to map srcva in envid's
360 // address space.
361 static int
362 sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
364 // LAB 4: Your code here.
365 struct Env *target;
366 struct Page *page;
367 pte_t *pte;
368 int r, ret = 0;
370 if ((r = envid2env(envid, &target, 0)) < 0)
371 return -E_BAD_ENV;
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
378 if (srcva) {
379 if ((unsigned int)srcva >= UTOP)
380 return -E_INVAL;
382 if (srcva != ROUNDDOWN(srcva, PGSIZE))
383 return -E_INVAL;
385 if ((page = page_lookup(curenv->env_pgdir, srcva, &pte)) == NULL)
386 return -E_INVAL;
388 // PTE_U and PTE_P must be set
389 if (!(perm & PTE_U) || !(perm & PTE_P))
390 return -E_INVAL;
391 // other bits than PTE_{U,P,W,AVAIL} are set
392 if (perm & ((~(PTE_U | PTE_P | PTE_W | PTE_AVAIL)) & 0xfff))
393 return -E_INVAL;
394 // perm has PTE_W, but scrpte is read-only.
395 if ((perm & PTE_W) && !(*pte & PTE_W))
396 return -E_INVAL;
398 if (page_insert(target->env_pgdir, page, target->env_ipc_dstva, perm) < 0)
399 return -E_NO_MEM;
401 ret = 1;
404 target->env_ipc_recving = 0;
405 target->env_ipc_value = value;
406 target->env_ipc_from = curenv->env_id;
407 if (ret)
408 target->env_ipc_perm = perm;
409 else
410 target->env_ipc_perm = 0;
411 target->env_status = ENV_RUNNABLE;
413 return ret;
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.
427 static int
428 sys_ipc_recv(void *dstva)
430 // LAB 4: Your code here.
431 if ((unsigned int)dstva >= UTOP || dstva != ROUNDDOWN(dstva, PGSIZE))
432 return -E_INVAL;
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;
442 // give up the CPU
443 sched_yield();
444 return 0;
447 static int
448 sys_phy_page(envid_t envid, void *va)
450 struct Env *task;
451 struct Page *page;
452 pte_t *pte;
454 if (envid2env(envid, &task, 1) < 0)
455 return -E_BAD_ENV;
457 page = page_lookup(task->env_pgdir, va, &pte);
458 if (page == 0)
459 return 0;
461 return *pte;
464 // Dispatches to the correct kernel function, passing the arguments.
465 int32_t
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.
471 int ret = 0;
473 switch (syscallno) {
474 case SYS_cputs:
475 sys_cputs((const char *)a1, (size_t)a2);
476 break;
477 case SYS_cgetc:
478 ret = sys_cgetc();
479 break;
480 case SYS_getenvid:
481 ret = sys_getenvid();
482 break;
483 case SYS_env_destroy:
484 ret = sys_env_destroy((envid_t)a1);
485 break;
486 case SYS_page_alloc:
487 ret = sys_page_alloc((envid_t)a1, (void *)a2, (int)a3);
488 break;
489 case SYS_page_map:
490 ret = sys_page_map((envid_t)a1, (void *)a2,
491 (envid_t)a3, (void *)a4, (int)a5);
492 break;
493 case SYS_page_unmap:
494 ret = sys_page_unmap((envid_t)a1, (void *)a2);
495 break;
496 case SYS_exofork:
497 ret = sys_exofork();
498 break;
499 case SYS_env_set_status:
500 ret = sys_env_set_status((envid_t)a1, (int)a2);
501 break;
502 case SYS_env_set_trapframe:
503 ret = sys_env_set_trapframe((envid_t)a1, (struct Trapframe *)a2);
504 break;
505 case SYS_env_set_pgfault_upcall:
506 ret = sys_env_set_pgfault_upcall((envid_t)a1, (void *)a2);
507 break;
508 case SYS_yield:
509 sys_yield();
510 break;
511 case SYS_phy_page:
512 ret = sys_phy_page((envid_t)a1, (void *)a2);
513 break;
514 case SYS_ipc_try_send:
515 ret = sys_ipc_try_send((envid_t)a1, (uint32_t)a2,
516 (void *)a3, (int)a4);
517 break;
518 case SYS_ipc_recv:
519 ret = sys_ipc_recv((void *)a1);
520 break;
521 default:
522 // NSYSCALLS
523 ret = -E_INVAL;
524 break;
527 return ret;