thanks to damocles, two places changed. one relates to the performance boost.
[mit-jos.git] / kern / syscall.c
blob457e6a9299aaeeee57cf474145af71162233afaa
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 if (e == curenv)
66 cprintf("[%08x] exiting gracefully\n", curenv->env_id);
67 else
68 cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id);
69 env_destroy(e);
70 return 0;
73 // Deschedule current environment and pick a different one to run.
74 static void
75 sys_yield(void)
77 sched_yield();
80 // Allocate a new environment.
81 // Returns envid of new environment, or < 0 on error. Errors are:
82 // -E_NO_FREE_ENV if no free environment is available.
83 static envid_t
84 sys_exofork(void)
86 // Create the new environment with env_alloc(), from kern/env.c.
87 // It should be left as env_alloc created it, except that
88 // status is set to ENV_NOT_RUNNABLE, and the register set is copied
89 // from the current environment -- but tweaked so sys_exofork
90 // will appear to return 0.
91 // LAB 4: Your code here.
92 struct Env *child;
94 if (env_alloc(&child, curenv->env_id) < 0)
95 return -E_NO_FREE_ENV;
97 child->env_status = ENV_NOT_RUNNABLE;
98 child->env_tf = curenv->env_tf;
99 // install the pgfault upcall to the child
100 child->env_pgfault_upcall = curenv->env_pgfault_upcall;
101 // tweak the register eax of the child,
102 // thus, the child will look like the return value
103 // of the the system call is zero.
104 child->env_tf.tf_regs.reg_eax = 0;
105 // but notice that the return value of the parent
106 // is the env id of the child
107 return child->env_id;
110 // Set envid's env_status to status, which must be ENV_RUNNABLE
111 // or ENV_NOT_RUNNABLE.
113 // Returns 0 on success, < 0 on error. Errors are:
114 // -E_BAD_ENV if environment envid doesn't currently exist,
115 // or the caller doesn't have permission to change envid.
116 // -E_INVAL if status is not a valid status for an environment.
117 static int
118 sys_env_set_status(envid_t envid, int status)
120 // Hint: Use the 'envid2env' function from kern/env.c to translate an
121 // envid to a struct Env.
122 // You should set envid2env's third argument to 1, which will
123 // check whether the current environment has permission to set
124 // envid's status.
125 // LAB 4: Your code here.
126 int r;
127 struct Env *task;
129 if ((r = envid2env(envid, &task, 1)) < 0)
130 return -E_BAD_ENV;
132 if (status != ENV_FREE &&
133 status != ENV_RUNNABLE &&
134 status != ENV_NOT_RUNNABLE)
135 return -E_INVAL;
137 task->env_status = status;
139 return 0;
142 // Set envid's trap frame to 'tf'.
143 // tf is modified to make sure that user environments always run at code
144 // protection level 3 (CPL 3) with interrupts enabled.
146 // Returns 0 on success, < 0 on error. Errors are:
147 // -E_BAD_ENV if environment envid doesn't currently exist,
148 // or the caller doesn't have permission to change envid.
149 static int
150 sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
152 // LAB 4: Your code here.
153 // Remember to check whether the user has supplied us with a good
154 // address!
155 panic("sys_set_trapframe not implemented");
158 // Set the page fault upcall for 'envid' by modifying the corresponding struct
159 // Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the
160 // kernel will push a fault record onto the exception stack, then branch to
161 // 'func'.
163 // Returns 0 on success, < 0 on error. Errors are:
164 // -E_BAD_ENV if environment envid doesn't currently exist,
165 // or the caller doesn't have permission to change envid.
166 static int
167 sys_env_set_pgfault_upcall(envid_t envid, void *func)
169 // LAB 4: Your code here.
170 struct Env *task;
172 if (envid2env(envid, &task, 1) < 0)
173 return -E_BAD_ENV;
175 task->env_pgfault_upcall = func;
177 return 0;
180 // Allocate a page of memory and map it at 'va' with permission
181 // 'perm' in the address space of 'envid'.
182 // The page's contents are set to 0.
183 // If a page is already mapped at 'va', that page is unmapped as a
184 // side effect.
186 // perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
187 // but no other bits may be set.
189 // Return 0 on success, < 0 on error. Errors are:
190 // -E_BAD_ENV if environment envid doesn't currently exist,
191 // or the caller doesn't have permission to change envid.
192 // -E_INVAL if va >= UTOP, or va is not page-aligned.
193 // -E_INVAL if perm is inappropriate (see above).
194 // -E_NO_MEM if there's no memory to allocate the new page,
195 // or to allocate any necessary page tables.
196 static int
197 sys_page_alloc(envid_t envid, void *va, int perm)
199 // Hint: This function is a wrapper around page_alloc() and
200 // page_insert() from kern/pmap.c.
201 // Most of the new code you write should be to check the
202 // parameters for correctness.
203 // If page_insert() fails, remember to free the page you
204 // allocated!
205 // LAB 4: Your code here.
206 struct Env *task;
207 struct Page *page;
209 //cprintf("sys_page_alloc: [%08x] .\n", envid);
210 if (envid2env(envid, &task, 1) < 0)
211 return -E_BAD_ENV;
213 if (page_alloc(&page) < 0)
214 return -E_NO_MEM;
216 if ((unsigned int)va >= UTOP || va != ROUNDDOWN(va, PGSIZE))
217 return -E_INVAL;
219 // PTE_U and PTE_P must be set
220 if (!(perm & PTE_U) || !(perm & PTE_P))
221 return -E_INVAL;
222 // other bits than PTE_{U,P,W,AVAIL} are set
223 if (perm & ((~(PTE_U | PTE_P | PTE_W | PTE_AVAIL)) & 0xfff))
224 return -E_INVAL;
226 memset(page2kva(page), 0, PGSIZE);
227 if (page_insert(task->env_pgdir, page, va, perm) < 0) {
228 page_free(page);
229 return -E_NO_MEM;
232 //cprintf("allocated page: [%08x].\n", page2pa(page));
233 return 0;
236 // Map the page of memory at 'srcva' in srcenvid's address space
237 // at 'dstva' in dstenvid's address space with permission 'perm'.
238 // Perm has the same restrictions as in sys_page_alloc, except
239 // that it also must not grant write access to a read-only
240 // page.
242 // Return 0 on success, < 0 on error. Errors are:
243 // -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
244 // or the caller doesn't have permission to change one of them.
245 // -E_INVAL if srcva >= UTOP or srcva is not page-aligned,
246 // or dstva >= UTOP or dstva is not page-aligned.
247 // -E_INVAL is srcva is not mapped in srcenvid's address space.
248 // -E_INVAL if perm is inappropriate (see sys_page_alloc).
249 // -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
250 // address space.
251 // -E_NO_MEM if there's no memory to allocate the new page,
252 // or to allocate any necessary page tables.
253 static int
254 sys_page_map(envid_t srcenvid, void *srcva,
255 envid_t dstenvid, void *dstva, int perm)
257 // Hint: This function is a wrapper around page_lookup() and
258 // page_insert() from kern/pmap.c.
259 // Again, most of the new code you write should be to check the
260 // parameters for correctness.
261 // Use the third argument to page_lookup() to
262 // check the current permissions on the page.
263 // LAB 4: Your code here.
264 struct Env *srcenv, *dstenv;
265 struct Page *page;
266 pte_t *srcpte, *dstpte;
268 if (envid2env(srcenvid, &srcenv, 1) < 0 ||
269 envid2env(dstenvid, &dstenv, 1) < 0)
270 return -E_BAD_ENV;
272 if ((unsigned int)srcva >= UTOP || srcva != ROUNDDOWN(srcva, PGSIZE) ||
273 (unsigned int)dstva >= UTOP || dstva != ROUNDDOWN(dstva, PGSIZE))
274 return -E_INVAL;
276 if ((page = page_lookup(srcenv->env_pgdir, srcva, &srcpte)) == NULL)
277 return -E_INVAL;
279 // PTE_U and PTE_P must be set
280 if (!(perm & PTE_U) || !(perm & PTE_P))
281 return -E_INVAL;
282 // other bits than PTE_{U,P,W,AVAIL} are set
283 if (perm & ((~(PTE_U | PTE_P | PTE_W | PTE_AVAIL)) & 0xfff))
284 return -E_INVAL;
285 // perm has PTE_W, but scrpte is read-only.
286 if ((perm & PTE_W) && !(*srcpte & PTE_W))
287 return -E_INVAL;
289 if (page_insert(dstenv->env_pgdir, page, dstva, perm) < 0)
290 return -E_NO_MEM;
291 /*cprintf("map [%08x] %08x(%08x) -> [%08x] %08x(%08x) perm: %x\n",
292 srcenv->env_id, srcva, *srcpte,
293 dstenv->env_id, dstva, *dstpte, perm);*/
295 return 0;
298 // Unmap the page of memory at 'va' in the address space of 'envid'.
299 // If no page is mapped, the function silently succeeds.
301 // Return 0 on success, < 0 on error. Errors are:
302 // -E_BAD_ENV if environment envid doesn't currently exist,
303 // or the caller doesn't have permission to change envid.
304 // -E_INVAL if va >= UTOP, or va is not page-aligned.
305 static int
306 sys_page_unmap(envid_t envid, void *va)
308 // Hint: This function is a wrapper around page_remove().
309 // LAB 4: Your code here.
310 struct Env *task;
312 if (envid2env(envid, &task, 1) < 0)
313 return -E_BAD_ENV;
315 if ((unsigned int)va >= UTOP || va != ROUNDDOWN(va, PGSIZE))
316 return -E_INVAL;
318 page_remove(task->env_pgdir, va);
320 return 0;
323 // Try to send 'value' to the target env 'envid'.
324 // If va != 0, then also send page currently mapped at 'va',
325 // so that receiver gets a duplicate mapping of the same page.
327 // The send fails with a return value of -E_IPC_NOT_RECV if the
328 // target has not requested IPC with sys_ipc_recv.
330 // Otherwise, the send succeeds, and the target's ipc fields are
331 // updated as follows:
332 // env_ipc_recving is set to 0 to block future sends;
333 // env_ipc_from is set to the sending envid;
334 // env_ipc_value is set to the 'value' parameter;
335 // env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise.
336 // The target environment is marked runnable again, returning 0
337 // from the paused ipc_recv system call.
339 // If the sender sends a page but the receiver isn't asking for one,
340 // then no page mapping is transferred, but no error occurs.
341 // The ipc doesn't happen unless no errors occur.
343 // Returns 0 on success where no page mapping occurs,
344 // 1 on success where a page mapping occurs, and < 0 on error.
345 // Errors are:
346 // -E_BAD_ENV if environment envid doesn't currently exist.
347 // (No need to check permissions.)
348 // -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
349 // or another environment managed to send first.
350 // -E_INVAL if srcva < UTOP but srcva is not page-aligned.
351 // -E_INVAL if srcva < UTOP and perm is inappropriate
352 // (see sys_page_alloc).
353 // -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's
354 // address space.
355 // -E_NO_MEM if there's not enough memory to map srcva in envid's
356 // address space.
357 static int
358 sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
360 // LAB 4: Your code here.
361 panic("sys_ipc_try_send not implemented");
364 // Block until a value is ready. Record that you want to receive
365 // using the env_ipc_recving and env_ipc_dstva fields of struct Env,
366 // mark yourself not runnable, and then give up the CPU.
368 // If 'dstva' is < UTOP, then you are willing to receive a page of data.
369 // 'dstva' is the virtual address at which the sent page should be mapped.
371 // This function only returns on error, but the system call will eventually
372 // return 0 on success.
373 // Return < 0 on error. Errors are:
374 // -E_INVAL if dstva < UTOP but dstva is not page-aligned.
375 static int
376 sys_ipc_recv(void *dstva)
378 // LAB 4: Your code here.
379 panic("sys_ipc_recv not implemented");
380 return 0;
383 static int
384 sys_phy_page(envid_t envid, void *va)
386 struct Env *task;
387 struct Page *page;
388 pte_t *pte;
390 if (envid2env(envid, &task, 1) < 0)
391 return -E_BAD_ENV;
393 page = page_lookup(task->env_pgdir, va, &pte);
394 if (page == 0)
395 return 0;
397 return *pte;
400 // Dispatches to the correct kernel function, passing the arguments.
401 int32_t
402 syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
404 // Call the function corresponding to the 'syscallno' parameter.
405 // Return any appropriate return value.
406 // LAB 3: Your code here.
407 int ret = 0;
409 switch (syscallno) {
410 case SYS_cputs:
411 sys_cputs((const char *)a1, (size_t)a2);
412 break;
413 case SYS_cgetc:
414 ret = sys_cgetc();
415 break;
416 case SYS_getenvid:
417 ret = sys_getenvid();
418 break;
419 case SYS_env_destroy:
420 ret = sys_env_destroy((envid_t)a1);
421 break;
422 case SYS_page_alloc:
423 ret = sys_page_alloc((envid_t)a1, (void *)a2, (int)a3);
424 break;
425 case SYS_page_map:
426 ret = sys_page_map((envid_t)a1, (void *)a2,
427 (envid_t)a3, (void *)a4, (int)a5);
428 break;
429 case SYS_page_unmap:
430 ret = sys_page_unmap((envid_t)a1, (void *)a2);
431 break;
432 case SYS_exofork:
433 ret = sys_exofork();
434 break;
435 case SYS_env_set_status:
436 ret = sys_env_set_status((envid_t)a1, (int)a2);
437 break;
438 case SYS_env_set_pgfault_upcall:
439 ret = sys_env_set_pgfault_upcall((envid_t)a1, (void *)a2);
440 break;
441 case SYS_yield:
442 sys_yield();
443 break;
444 case SYS_phy_page:
445 ret = sys_phy_page((envid_t)a1, (void *)a2);
446 break;
447 default:
448 // NSYSCALLS
449 ret = -E_INVAL;
450 break;
453 return ret;