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)
66 cprintf("[%08x] exiting gracefully\n", curenv
->env_id
);
68 cprintf("[%08x] destroying %08x\n", curenv
->env_id
, e
->env_id
);
73 // Deschedule current environment and pick a different one to run.
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.
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.
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.
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
125 // LAB 4: Your code here.
129 if ((r
= envid2env(envid
, &task
, 1)) < 0)
132 if (status
!= ENV_FREE
&&
133 status
!= ENV_RUNNABLE
&&
134 status
!= ENV_NOT_RUNNABLE
)
137 task
->env_status
= status
;
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.
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
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
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.
167 sys_env_set_pgfault_upcall(envid_t envid
, void *func
)
169 // LAB 4: Your code here.
172 if (envid2env(envid
, &task
, 1) < 0)
175 task
->env_pgfault_upcall
= func
;
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
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.
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
205 // LAB 4: Your code here.
209 //cprintf("sys_page_alloc: [%08x] .\n", envid);
210 if (envid2env(envid
, &task
, 1) < 0)
213 if (page_alloc(&page
) < 0)
216 if ((unsigned int)va
>= UTOP
|| va
!= ROUNDDOWN(va
, PGSIZE
))
219 // PTE_U and PTE_P must be set
220 if (!(perm
& PTE_U
) || !(perm
& PTE_P
))
222 // other bits than PTE_{U,P,W,AVAIL} are set
223 if (perm
& ((~(PTE_U
| PTE_P
| PTE_W
| PTE_AVAIL
)) & 0xfff))
226 memset(page2kva(page
), 0, PGSIZE
);
227 if (page_insert(task
->env_pgdir
, page
, va
, perm
) < 0) {
232 //cprintf("allocated page: [%08x].\n", page2pa(page));
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
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
251 // -E_NO_MEM if there's no memory to allocate the new page,
252 // or to allocate any necessary page tables.
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
;
266 pte_t
*srcpte
, *dstpte
;
268 if (envid2env(srcenvid
, &srcenv
, 1) < 0 ||
269 envid2env(dstenvid
, &dstenv
, 1) < 0)
272 if ((unsigned int)srcva
>= UTOP
|| srcva
!= ROUNDDOWN(srcva
, PGSIZE
) ||
273 (unsigned int)dstva
>= UTOP
|| dstva
!= ROUNDDOWN(dstva
, PGSIZE
))
276 if ((page
= page_lookup(srcenv
->env_pgdir
, srcva
, &srcpte
)) == NULL
)
279 // PTE_U and PTE_P must be set
280 if (!(perm
& PTE_U
) || !(perm
& PTE_P
))
282 // other bits than PTE_{U,P,W,AVAIL} are set
283 if (perm
& ((~(PTE_U
| PTE_P
| PTE_W
| PTE_AVAIL
)) & 0xfff))
285 // perm has PTE_W, but scrpte is read-only.
286 if ((perm
& PTE_W
) && !(*srcpte
& PTE_W
))
289 if (page_insert(dstenv
->env_pgdir
, page
, dstva
, perm
) < 0)
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);*/
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.
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.
312 if (envid2env(envid
, &task
, 1) < 0)
315 if ((unsigned int)va
>= UTOP
|| va
!= ROUNDDOWN(va
, PGSIZE
))
318 page_remove(task
->env_pgdir
, va
);
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.
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
355 // -E_NO_MEM if there's not enough memory to map srcva in envid's
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.
376 sys_ipc_recv(void *dstva
)
378 // LAB 4: Your code here.
379 panic("sys_ipc_recv not implemented");
384 sys_phy_page(envid_t envid
, void *va
)
390 if (envid2env(envid
, &task
, 1) < 0)
393 page
= page_lookup(task
->env_pgdir
, va
, &pte
);
400 // Dispatches to the correct kernel function, passing the arguments.
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.
411 sys_cputs((const char *)a1
, (size_t)a2
);
417 ret
= sys_getenvid();
419 case SYS_env_destroy
:
420 ret
= sys_env_destroy((envid_t
)a1
);
423 ret
= sys_page_alloc((envid_t
)a1
, (void *)a2
, (int)a3
);
426 ret
= sys_page_map((envid_t
)a1
, (void *)a2
,
427 (envid_t
)a3
, (void *)a4
, (int)a5
);
430 ret
= sys_page_unmap((envid_t
)a1
, (void *)a2
);
435 case SYS_env_set_status
:
436 ret
= sys_env_set_status((envid_t
)a1
, (int)a2
);
438 case SYS_env_set_pgfault_upcall
:
439 ret
= sys_env_set_pgfault_upcall((envid_t
)a1
, (void *)a2
);
445 ret
= sys_phy_page((envid_t
)a1
, (void *)a2
);