forked from woai3c/MIT6.828
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsyscall.c
452 lines (406 loc) · 13.7 KB
/
syscall.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/* See COPYRIGHT for copyright information. */
#include <inc/x86.h>
#include <inc/error.h>
#include <inc/string.h>
#include <inc/assert.h>
#include <kern/env.h>
#include <kern/pmap.h>
#include <kern/trap.h>
#include <kern/syscall.h>
#include <kern/console.h>
#include <kern/sched.h>
#include <kern/time.h>
#include <kern/e1000.h>
#define debug 0
// Print a string to the system console.
// The string is exactly 'len' characters long.
// Destroys the environment on memory errors.
static void
sys_cputs(const char *s, size_t len)
{
// Check that the user has permission to read memory [s, s+len).
// Destroy the environment if not.
// LAB 3: Your code here.
user_mem_assert(curenv, s, len, 0);
// Print the string supplied by the user.
cprintf("%.*s", len, s);
}
// Read a character from the system console without blocking.
// Returns the character, or 0 if there is no input waiting.
static int
sys_cgetc(void)
{
return cons_getc();
}
// Returns the current environment's envid.
static envid_t
sys_getenvid(void)
{
return curenv->env_id;
}
// Destroy a given environment (possibly the currently running environment).
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist,
// or the caller doesn't have permission to change envid.
static int
sys_env_destroy(envid_t envid)
{
int r;
struct Env *e;
if ((r = envid2env(envid, &e, 1)) < 0)
return r;
env_destroy(e);
return 0;
}
// Deschedule current environment and pick a different one to run.
static void
sys_yield(void)
{
sched_yield();
}
// Allocate a new environment.
// Returns envid of new environment, or < 0 on error. Errors are:
// -E_NO_FREE_ENV if no free environment is available.
// -E_NO_MEM on memory exhaustion.
static envid_t
sys_exofork(void)
{
// Create the new environment with env_alloc(), from kern/env.c.
// It should be left as env_alloc created it, except that
// status is set to ENV_NOT_RUNNABLE, and the register set is copied
// from the current environment -- but tweaked so sys_exofork
// will appear to return 0.
// LAB 4: Your code here.
struct Env *child;
int status = env_alloc(&child, curenv->env_id);
if (status < 0) return status;
child->env_tf = curenv->env_tf;
child->env_status = ENV_NOT_RUNNABLE;
child->env_tf.tf_regs.reg_eax = 0; //新的用户环境从sys_exofork()的返回值应该为0
return child->env_id;
}
// Set envid's env_status to status, which must be ENV_RUNNABLE
// or ENV_NOT_RUNNABLE.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist,
// or the caller doesn't have permission to change envid.
// -E_INVAL if status is not a valid status for an environment.
static int
sys_env_set_status(envid_t envid, int status)
{
// Hint: Use the 'envid2env' function from kern/env.c to translate an
// envid to a struct Env.
// You should set envid2env's third argument to 1, which will
// check whether the current environment has permission to set
// envid's status.
// LAB 4: Your code here.
struct Env *env;
int state = envid2env(envid, &env, 1);
if (state < 0) return state;
if (status != ENV_NOT_RUNNABLE && status != ENV_RUNNABLE) {
return -E_INVAL;
}
env->env_status = status;
return 0;
}
// Set envid's trap frame to 'tf'.
// tf is modified to make sure that user environments always run at code
// protection level 3 (CPL 3), interrupts enabled, and IOPL of 0.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist,
// or the caller doesn't have permission to change envid.
static int
sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
{
// LAB 5: Your code here.
// Remember to check whether the user has supplied us with a good
// address!
struct Env *env;
int r;
if ((r = envid2env(envid, &env, 1)) < 0) return r;
tf->tf_eflags |= FL_IF;
tf->tf_eflags &= ~FL_IOPL_MASK;
tf->tf_cs = GD_UT | 3;
env->env_tf = *tf;
return 0;
}
// Set the page fault upcall for 'envid' by modifying the corresponding struct
// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the
// kernel will push a fault record onto the exception stack, then branch to
// 'func'.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist,
// or the caller doesn't have permission to change envid.
static int
sys_env_set_pgfault_upcall(envid_t envid, void *func)
{
// LAB 4: Your code here.
struct Env *env;
int state = envid2env(envid, &env, 1);
if (state < 0) return state;
env->env_pgfault_upcall = func;
return 0;
}
// Allocate a page of memory and map it at 'va' with permission
// 'perm' in the address space of 'envid'.
// The page's contents are set to 0.
// If a page is already mapped at 'va', that page is unmapped as a
// side effect.
//
// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h.
//
// Return 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist,
// or the caller doesn't have permission to change envid.
// -E_INVAL if va >= UTOP, or va is not page-aligned.
// -E_INVAL if perm is inappropriate (see above).
// -E_NO_MEM if there's no memory to allocate the new page,
// or to allocate any necessary page tables.
static int
sys_page_alloc(envid_t envid, void *va, int perm)
{
// Hint: This function is a wrapper around page_alloc() and
// page_insert() from kern/pmap.c.
// Most of the new code you write should be to check the
// parameters for correctness.
// If page_insert() fails, remember to free the page you
// allocated!
// LAB 4: Your code here.
struct Env *env;
int state = envid2env(envid, &env, 1);
if (state < 0) return state;
if ((size_t) va >= UTOP || ((size_t) va % PGSIZE) != 0) return -E_INVAL;
if ((perm & PTE_U) != PTE_U || (perm & PTE_P) != PTE_P) return -E_INVAL;
struct PageInfo *pp = page_alloc(1);
if (pp == NULL) return -E_NO_MEM;
if (page_insert(env->env_pgdir, pp, va, perm) < 0) {
page_free(pp);
return -E_NO_MEM;
}
return 0;
}
// Map the page of memory at 'srcva' in srcenvid's address space
// at 'dstva' in dstenvid's address space with permission 'perm'.
// Perm has the same restrictions as in sys_page_alloc, except
// that it also must not grant write access to a read-only
// page.
//
// Return 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
// or the caller doesn't have permission to change one of them.
// -E_INVAL if srcva >= UTOP or srcva is not page-aligned,
// or dstva >= UTOP or dstva is not page-aligned.
// -E_INVAL is srcva is not mapped in srcenvid's address space.
// -E_INVAL if perm is inappropriate (see sys_page_alloc).
// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
// address space.
// -E_NO_MEM if there's no memory to allocate any necessary page tables.
static int
sys_page_map(envid_t srcenvid, void *srcva,
envid_t dstenvid, void *dstva, int perm)
{
// Hint: This function is a wrapper around page_lookup() and
// page_insert() from kern/pmap.c.
// Again, most of the new code you write should be to check the
// parameters for correctness.
// Use the third argument to page_lookup() to
// check the current permissions on the page.
// LAB 4: Your code here.
if ((size_t) srcva >= UTOP || ((size_t) srcva % PGSIZE) != 0 || (size_t) dstva >= UTOP || ((size_t) dstva % PGSIZE) != 0) {
return -E_INVAL;
}
struct Env *senv, *denv;
int state1, state2;
state1 = envid2env(srcenvid, &senv, 1);
state2 = envid2env(dstenvid, &denv, 1);
if (state1 < 0) return state1;
if (state2 < 0) return state2;
pte_t *pte;
struct PageInfo *pp = page_lookup(senv->env_pgdir, srcva, &pte);
if (pp == NULL) return -E_INVAL;
if (((*pte & PTE_W) == 0) && (perm & PTE_W)) return -E_INVAL;
return page_insert(denv->env_pgdir, pp, dstva, perm);
}
// Unmap the page of memory at 'va' in the address space of 'envid'.
// If no page is mapped, the function silently succeeds.
//
// Return 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist,
// or the caller doesn't have permission to change envid.
// -E_INVAL if va >= UTOP, or va is not page-aligned.
static int
sys_page_unmap(envid_t envid, void *va)
{
// Hint: This function is a wrapper around page_remove().
// LAB 4: Your code here.
struct Env *env;
if (envid2env(envid, &env, 1) < 0) return -E_BAD_ENV;
if ((size_t) va >= UTOP || ((size_t) va % PGSIZE) != 0) {
return -E_INVAL;
}
page_remove(env->env_pgdir, va);
return 0;
}
// Try to send 'value' to the target env 'envid'.
// If srcva < UTOP, then also send page currently mapped at 'srcva',
// so that receiver gets a duplicate mapping of the same page.
//
// The send fails with a return value of -E_IPC_NOT_RECV if the
// target is not blocked, waiting for an IPC.
//
// The send also can fail for the other reasons listed below.
//
// Otherwise, the send succeeds, and the target's ipc fields are
// updated as follows:
// env_ipc_recving is set to 0 to block future sends;
// env_ipc_from is set to the sending envid;
// env_ipc_value is set to the 'value' parameter;
// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise.
// The target environment is marked runnable again, returning 0
// from the paused sys_ipc_recv system call. (Hint: does the
// sys_ipc_recv function ever actually return?)
//
// If the sender wants to send a page but the receiver isn't asking for one,
// then no page mapping is transferred, but no error occurs.
// The ipc only happens when no errors occur.
//
// Returns 0 on success, < 0 on error.
// Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist.
// (No need to check permissions.)
// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
// or another environment managed to send first.
// -E_INVAL if srcva < UTOP but srcva is not page-aligned.
// -E_INVAL if srcva < UTOP and perm is inappropriate
// (see sys_page_alloc).
// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's
// address space.
// -E_INVAL if (perm & PTE_W), but srcva is read-only in the
// current environment's address space.
// -E_NO_MEM if there's not enough memory to map srcva in envid's
// address space.
static int
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
{
// LAB 4: Your code here.
struct Env *env;
if (envid2env(envid, &env, 0) < 0) return -E_BAD_ENV;
if (!env->env_ipc_recving) return -E_IPC_NOT_RECV;
if ((size_t) srcva < UTOP) {
if (((size_t) srcva % PGSIZE) != 0) {
return -E_INVAL;
}
if ((perm & PTE_U) != PTE_U || (perm & PTE_P) != PTE_P) return -E_INVAL;
pte_t *pte;
struct PageInfo *pp = page_lookup(curenv->env_pgdir, srcva, &pte);
if (!pp) return -E_INVAL;
if ((perm & PTE_W) && ((size_t) *pte & PTE_W) != PTE_W) return -E_INVAL;
if ((size_t) env->env_ipc_dstva < UTOP) {
if (page_insert(env->env_pgdir, pp, env->env_ipc_dstva, perm) < 0) return -E_NO_MEM;
env->env_ipc_perm = perm;
}
} else {
env->env_ipc_perm = 0;
}
env->env_ipc_from = curenv->env_id;
env->env_ipc_recving = 0;
env->env_ipc_value = value;
env->env_status = ENV_RUNNABLE;
env->env_tf.tf_regs.reg_eax = 0;
return 0;
}
// Block until a value is ready. Record that you want to receive
// using the env_ipc_recving and env_ipc_dstva fields of struct Env,
// mark yourself not runnable, and then give up the CPU.
//
// If 'dstva' is < UTOP, then you are willing to receive a page of data.
// 'dstva' is the virtual address at which the sent page should be mapped.
//
// This function only returns on error, but the system call will eventually
// return 0 on success.
// Return < 0 on error. Errors are:
// -E_INVAL if dstva < UTOP but dstva is not page-aligned.
static int
sys_ipc_recv(void *dstva)
{
// LAB 4: Your code here.
if ((size_t) dstva < UTOP && ((size_t) dstva % PGSIZE) != 0) return -E_INVAL;
curenv->env_ipc_recving = 1;
curenv->env_ipc_dstva = dstva;
curenv->env_status = ENV_NOT_RUNNABLE;
sys_yield();
return 0;
}
// Return the current time.
static int
sys_time_msec(void)
{
// LAB 6: Your code here.
return time_msec();
}
int
sys_pkt_send(void *data, size_t len)
{
return e1000_transmit(data, len);
}
static int
sys_pkt_recv(void *addr, size_t *len)
{
return e1000_receive(addr, len);
}
// Dispatches to the correct kernel function, passing the arguments.
int32_t
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
{
// Call the function corresponding to the 'syscallno' parameter.
// Return any appropriate return value.
// LAB 3: Your code here.
switch (syscallno) {
case SYS_cputs:
sys_cputs((char *) a1, a2);
return 0;
case SYS_cgetc:
return sys_cgetc();
case SYS_getenvid:
return sys_getenvid();
case SYS_env_destroy:
return sys_env_destroy(a1);
case SYS_yield:
sys_yield();
return 0;
case SYS_exofork:
return sys_exofork();
case SYS_env_set_status:
return sys_env_set_status(a1, a2);
case SYS_page_alloc:
return sys_page_alloc(a1, (void *) a2, a3);
case SYS_page_map:
return sys_page_map(a1, (void *) a2, a3, (void *) a4, a5);
case SYS_page_unmap:
return sys_page_unmap(a1, (void *) a2);
case SYS_env_set_pgfault_upcall:
return sys_env_set_pgfault_upcall(a1, (void *) a2);
case SYS_ipc_try_send:
return sys_ipc_try_send(a1, a2, (void *) a3, a4);
break;
case SYS_ipc_recv:
return sys_ipc_recv((void *) a1);
break;
case SYS_env_set_trapframe:
return sys_env_set_trapframe(a1, (struct Trapframe *) a2);
case SYS_time_msec:
return sys_time_msec();
case SYS_pkt_send:
return sys_pkt_send((void *)a1, (size_t)a2);
case SYS_pkt_recv:
return sys_pkt_recv((void *)a1, (size_t *)a2);
default:
return -E_INVAL;
}
return -E_INVAL;
}