1 /*
2 * Copyright (c) 2013-2023 Ali Mashtizadeh
3 * All rights reserved.
4 */
5
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <errno.h>
11
12 #include <sys/kassert.h>
13 #include <sys/kmem.h>
14 #include <sys/ktime.h>
15 #include <sys/ktimer.h>
16 #include <sys/thread.h>
17 #include <sys/loader.h>
18 #include <sys/syscall.h>
19 #include <sys/disk.h>
20 #include <sys/vfs.h>
21 #include <sys/vfsuio.h>
22 #include <sys/nic.h>
23 #include <sys/sysctl.h>
24
25 Handle *Console_OpenHandle();
26
27 uint64_t
Syscall_Time()28 Syscall_Time()
29 {
30 return KTime_GetEpochNS();
31 }
32
33 uint64_t
Syscall_GetPID()34 Syscall_GetPID()
35 {
36 Thread *cur = Sched_Current();
37 uint64_t pid = cur->proc->pid;
38
39 Thread_Release(cur);
40
41 return pid;
42 }
43
44 void
Syscall_Exit(uint64_t status)45 Syscall_Exit(uint64_t status)
46 {
47 Thread *cur = Sched_Current();
48
49 // Request each thread to exit
50
51 // Wait for all threads to exit
52
53 // Write exit code
54 cur->proc->exitCode = status;
55
56 // Exit this thread
57 Sched_SetZombie(cur);
58 Thread_Release(cur);
59 Sched_Scheduler();
60
61 // Should not return
62 Panic("Returned to exited thread!\n");
63
64 return;
65 }
66
67 uint64_t
Syscall_Spawn(uint64_t user_path,uint64_t user_argv)68 Syscall_Spawn(uint64_t user_path, uint64_t user_argv)
69 {
70 int status;
71 char path[512];
72 void *pg;
73 char *arg;
74 VNode *file;
75 Process *proc;
76 Thread *thr;
77 Thread *cur;
78
79 status = Copy_StrIn(user_path, &path, sizeof(path));
80 if (status != 0)
81 return SYSCALL_PACK(status, 0);
82
83 Log(syscall, "Spawn(%s)\n", path);
84
85 arg = PAlloc_AllocPage();
86 if (!arg) {
87 return SYSCALL_PACK(ENOMEM, 0);
88 }
89
90 /* Copy argument pointers */
91 for (int i = 0; i < 8; i++) {
92 uintptr_t off = sizeof(uintptr_t)*i;
93
94 status = Copy_In(user_argv+off, arg+sizeof(uintptr_t)*(1+i), sizeof(uintptr_t));
95 if (status != 0) {
96 PAlloc_Release(arg);
97 return SYSCALL_PACK(status, 0);
98 }
99
100 if (*(uintptr_t *)(arg+sizeof(uintptr_t)*(1+i)) == 0)
101 break;
102 }
103
104 /* Copy each argument in */
105 char *argstart = arg+sizeof(uintptr_t)*8;
106 for (int i = 1; i < 8; i++) {
107 uintptr_t *str = (uintptr_t *)(arg+sizeof(uintptr_t)*i);
108 if (*str == 0)
109 break;
110
111 status = Copy_StrIn(*str, argstart, 256); // XXX: Make sure there's no overrun
112 if (status != 0) {
113 PAlloc_Release(arg);
114 return SYSCALL_PACK(status, 0);
115 }
116
117 *str = (uintptr_t)argstart;
118 argstart += strlen(argstart)+1;
119 }
120
121 pg = PAlloc_AllocPage();
122 if (!pg) {
123 PAlloc_Release(arg);
124 return SYSCALL_PACK(ENOMEM, 0);
125 }
126
127 /* XXXFILLMEIN: Load the ELF headers into the page. */
128
129 if (!Loader_CheckHeader(pg)) {
130 VFS_Close(file);
131 PAlloc_Release(pg);
132 PAlloc_Release(arg);
133 return SYSCALL_PACK(EINVAL, 0);
134 }
135
136 cur = Sched_Current();
137 proc = Process_Create(cur->proc, path);
138 thr = Thread_Create(proc);
139 Thread_Release(cur);
140 Log(syscall, "SPAWN %lx\n", thr);
141
142 Handle *handle = Console_OpenHandle();
143 Handle_Add(proc, handle);
144 handle = Console_OpenHandle();
145 Handle_Add(proc, handle);
146 handle = Console_OpenHandle();
147 Handle_Add(proc, handle);
148
149 Loader_Load(thr, file, pg, 1024);
150
151 /* Initialize the trap frame for entering into the process. */
152 Thread_SetupUThread(thr, proc->entrypoint, MEM_USERSPACE_STKTOP - PGSIZE);
153
154 /* Translate mapping for stack page */
155 argstart = (char *)DMPA2VA(PMap_Translate(thr->space, MEM_USERSPACE_STKTOP - PGSIZE));
156 uintptr_t offset = sizeof(uintptr_t)*8;
157
158 /* XXXFILLMEIN: Export the argument array out to the new application. */
159
160 Sched_SetRunnable(thr);
161
162 return SYSCALL_PACK(0, proc->pid);
163 }
164
165 uint64_t
Syscall_Wait(uint64_t pid)166 Syscall_Wait(uint64_t pid)
167 {
168 uint64_t status;
169 Thread *cur = Sched_Current();
170
171 status = Process_Wait(cur->proc, pid);
172 Thread_Release(cur);
173
174 return status;
175 }
176
177 uint64_t
Syscall_MMap(uint64_t addr,uint64_t len,uint64_t prot)178 Syscall_MMap(uint64_t addr, uint64_t len, uint64_t prot)
179 {
180 Thread *cur = Sched_Current();
181 bool status;
182
183 status = PMap_AllocMap(cur->space, addr, len, PTE_W);
184 Thread_Release(cur);
185 if (!status) {
186 // XXX: Need to unmap PMap_Unmap(cur->space, addr, pgs);
187 return 0;
188 } else {
189 return addr;
190 }
191 }
192
193 uint64_t
Syscall_MUnmap(uint64_t addr,uint64_t len)194 Syscall_MUnmap(uint64_t addr, uint64_t len)
195 {
196 Thread *cur = Sched_Current();
197 uint64_t p;
198
199 for (p = 0; p < len; p += PGSIZE)
200 {
201 // Free page
202 }
203
204 PMap_Unmap(cur->space, addr, len /= PGSIZE);
205 Thread_Release(cur);
206
207 return 0;
208 }
209
210 uint64_t
Syscall_MProtect(uint64_t addr,uint64_t len,uint64_t prot)211 Syscall_MProtect(uint64_t addr, uint64_t len, uint64_t prot)
212 {
213 //Thread *cur = Sched_Current();
214 NOT_IMPLEMENTED();
215 return 0;
216 }
217
218 uint64_t
Syscall_Read(uint64_t fd,uint64_t addr,uint64_t off,uint64_t length)219 Syscall_Read(uint64_t fd, uint64_t addr, uint64_t off, uint64_t length)
220 {
221 uint64_t status;
222 Thread *cur = Sched_Current();
223 Handle *handle = Handle_Lookup(cur->proc, fd);
224
225 if (handle == NULL) {
226 status = -EBADF;
227 } else {
228 status = (handle->read)(handle, (void *)addr, off, length);
229 }
230
231 Thread_Release(cur);
232
233 return status;
234 }
235
236 uint64_t
Syscall_Write(uint64_t fd,uint64_t addr,uint64_t off,uint64_t length)237 Syscall_Write(uint64_t fd, uint64_t addr, uint64_t off, uint64_t length)
238 {
239 uint64_t status;
240 Thread *cur = Sched_Current();
241 Handle *handle = Handle_Lookup(cur->proc, fd);
242
243 if (handle == NULL) {
244 status = -EBADF;
245 } else {
246 status = (handle->write)(handle, (void *)addr, off, length);
247 }
248
249 Thread_Release(cur);
250
251 return status;
252 }
253
254 uint64_t
Syscall_Flush(uint64_t fd)255 Syscall_Flush(uint64_t fd)
256 {
257 uint64_t status;
258 Thread *cur = Sched_Current();
259 Handle *handle = Handle_Lookup(cur->proc, fd);
260
261 if (handle == NULL) {
262 status = -EBADF;
263 } else {
264 status = (handle->flush)(handle);
265 }
266
267 Thread_Release(cur);
268
269 return status;
270 }
271
272 // XXX: Cleanup
273 Handle *Console_OpenHandle();
274
275 uint64_t
Syscall_Open(uint64_t user_path,uint64_t flags)276 Syscall_Open(uint64_t user_path, uint64_t flags)
277 {
278 uint64_t handleNo;
279 Thread *cur = Sched_Current();
280 int status;
281 char path[256];
282
283 status = Copy_StrIn(user_path, &path, sizeof(path));
284 if (status != 0) {
285 Thread_Release(cur);
286 return status;
287 }
288
289 if (strncmp("/dev/", path, 5) == 0) {
290 if (strcmp("/dev/console", path) == 0) {
291 Handle *handle = Console_OpenHandle();
292 handleNo = Handle_Add(cur->proc, handle);
293 Thread_Release(cur);
294 return handleNo;
295 }
296
297 Thread_Release(cur);
298 return -ENOENT;
299 }
300
301 Handle *handle;
302 status = VFSUIO_Open(path, &handle);
303 if (status != 0) {
304 Thread_Release(cur);
305 return status;
306 }
307
308 handleNo = Handle_Add(cur->proc, handle);
309 Thread_Release(cur);
310 return handleNo;
311 }
312
313 uint64_t
Syscall_Close(uint64_t fd)314 Syscall_Close(uint64_t fd)
315 {
316 uint64_t status;
317 Thread *cur = Sched_Current();
318 Handle *handle = Handle_Lookup(cur->proc, fd);
319
320 if (handle == NULL) {
321 status = -EBADF;
322 } else {
323 status = (handle->close)(handle);
324 }
325
326 Thread_Release(cur);
327
328 return status;
329 }
330
331 uint64_t
Syscall_Stat(uint64_t user_path,uint64_t user_stat)332 Syscall_Stat(uint64_t user_path, uint64_t user_stat)
333 {
334 int status;
335 char path[256];
336 struct stat sb;
337
338 status = Copy_StrIn(user_path, &path, sizeof(path));
339 if (status != 0) {
340 return status;
341 }
342
343 // VFS_Stat
344 status = VFS_Stat(path, &sb);
345 if (status != 0) {
346 return status;
347 }
348
349 status = Copy_Out(&sb, user_stat, sizeof(struct stat));
350 if (status != 0) {
351 return status;
352 }
353
354 return 0;
355 }
356
357 uint64_t
Syscall_ReadDir(uint64_t fd,char * user_buf,size_t len,uintptr_t user_off)358 Syscall_ReadDir(uint64_t fd, char *user_buf, size_t len, uintptr_t user_off)
359 {
360 int status, rstatus;
361 Thread *cur = Sched_Current();
362 Handle *handle = Handle_Lookup(cur->proc, fd);
363 uint64_t offset;
364
365 if (handle == NULL) {
366 Thread_Release(cur);
367 return -EBADF;
368 }
369
370 status = Copy_In(user_off, &offset, sizeof(offset));
371 if (status != 0) {
372 Thread_Release(cur);
373 return status;
374 }
375
376 if (handle->type != HANDLE_TYPE_FILE) {
377 Thread_Release(cur);
378 return -ENOTDIR;
379 }
380
381 rstatus = VFS_ReadDir(handle->vnode, user_buf, len, &offset);
382 if (rstatus < 0) {
383 Thread_Release(cur);
384 return rstatus;
385 }
386
387 status = Copy_Out(&offset, user_off, sizeof(offset));
388 if (status != 0) {
389 Thread_Release(cur);
390 return status;
391 }
392
393 Thread_Release(cur);
394
395 return rstatus;
396 }
397
398 uint64_t
Syscall_ThreadCreate(uint64_t rip,uint64_t arg)399 Syscall_ThreadCreate(uint64_t rip, uint64_t arg)
400 {
401 uint64_t threadId;
402 Thread *curThread = Sched_Current();
403 Thread *newThread = Thread_UThreadCreate(curThread, rip, arg);
404
405 Thread_Release(curThread);
406 if (newThread == NULL) {
407 return SYSCALL_PACK(ENOMEM, 0);
408 }
409
410 threadId = newThread->tid;
411 Sched_SetRunnable(newThread);
412
413 return SYSCALL_PACK(0, threadId);
414 }
415
416 uint64_t
Syscall_GetTID()417 Syscall_GetTID()
418 {
419 Thread *cur = Sched_Current();
420 uint64_t tid = cur->tid;
421
422 Thread_Release(cur);
423
424 return tid;
425 }
426
427 void
Syscall_ThreadExit(uint64_t status)428 Syscall_ThreadExit(uint64_t status)
429 {
430 Thread *cur = Sched_Current();
431
432 // Encode this like POSIX
433 cur->exitValue = status;
434
435 Sched_SetZombie(cur);
436 Semaphore_Release(&cur->proc->zombieSemaphore);
437 Thread_Release(cur);
438 Sched_Scheduler();
439
440 // Should not return
441 Panic("Returned to exited thread!\n");
442 }
443
444 static void
ThreadWakeupHelper(void * arg)445 ThreadWakeupHelper(void *arg)
446 {
447 Thread *thr = (Thread *)arg;
448
449 Sched_SetRunnable(thr);
450 KTimer_Release(thr->timerEvt);
451 thr->timerEvt = NULL;
452 Thread_Release(thr);
453 }
454
455 uint64_t
Syscall_ThreadSleep(uint64_t time)456 Syscall_ThreadSleep(uint64_t time)
457 {
458 Thread *cur = Sched_Current();
459
460 // If the sleep time is zero just yield
461 if (time != 0) {
462 Thread_Retain(cur);
463 cur->timerEvt = KTimer_Create(time, ThreadWakeupHelper, cur);
464 if (cur->timerEvt == NULL) {
465 Thread_Release(cur);
466 Thread_Release(cur);
467 return -ENOMEM;
468 }
469
470 Sched_SetWaiting(cur);
471 }
472 Sched_Scheduler();
473
474 Thread_Release(cur);
475
476 return 0;
477 }
478
479 uint64_t
Syscall_ThreadWait(uint64_t tid)480 Syscall_ThreadWait(uint64_t tid)
481 {
482 uint64_t status;
483 Thread *cur = Sched_Current();
484
485 /*
486 * Acquire the zombie semaphore see if the specified thread has exited or
487 * any thread if tid == 0. If the specified thread hasn't exited wait
488 * again on the semaphore. POSIX does not give any guarentees if multiple
489 * threads wait on the same thread and neither do we.
490 *
491 * As a precaution we call Sched_Scheduler to prevent looping on the
492 * semaphore acquire-release.
493 */
494 while (1) {
495 Semaphore_Acquire(&cur->proc->zombieSemaphore);
496 status = Thread_Wait(cur, tid);
497 if (SYSCALL_ERRCODE(status) != EAGAIN) {
498 Thread_Release(cur);
499 return status;
500 }
501 Semaphore_Release(&cur->proc->zombieSemaphore);
502 Sched_Scheduler();
503 }
504 }
505
506 uint64_t
Syscall_NICStat(uint64_t nicNo,uint64_t user_stat)507 Syscall_NICStat(uint64_t nicNo, uint64_t user_stat)
508 {
509 int status;
510 NIC *nic;
511
512 nic = NIC_GetByID(nicNo);
513 if (nic == NULL) {
514 return ENOENT;
515 }
516
517 status = Copy_Out(nic, user_stat, sizeof(NIC));
518 if (status != 0) {
519 return status;
520 }
521
522 return 0;
523 }
524
525 uint64_t
Syscall_NICSend(uint64_t nicNo,uint64_t user_mbuf)526 Syscall_NICSend(uint64_t nicNo, uint64_t user_mbuf)
527 {
528 int status;
529 NIC *nic;
530 MBuf mbuf;
531
532 status = Copy_In(user_mbuf, &mbuf, sizeof(mbuf));
533 if (status != 0) {
534 return SYSCALL_PACK(status, 0);
535 }
536
537 nic = NIC_GetByID(nicNo);
538 if (nic == NULL) {
539 return SYSCALL_PACK(ENOENT, 0);
540 }
541
542 // Pin Memory
543 (nic->tx)(nic, &mbuf, NULL, NULL);
544 // Unpin Memory
545
546 return 0;
547 }
548
549 uint64_t
Syscall_NICRecv(uint64_t nicNo,uint64_t user_mbuf)550 Syscall_NICRecv(uint64_t nicNo, uint64_t user_mbuf)
551 {
552 int status;
553 NIC *nic;
554 MBuf mbuf;
555
556 status = Copy_In(user_mbuf, &mbuf, sizeof(mbuf));
557 if (status != 0) {
558 return SYSCALL_PACK(status, 0);
559 }
560
561 nic = NIC_GetByID(nicNo);
562 if (nic == NULL) {
563 return SYSCALL_PACK(ENOENT, 0);
564 }
565
566 // Pin Memory
567 (nic->rx)(nic, &mbuf, NULL, NULL);
568 // Unpin Memory
569
570 return 0;
571 }
572
573 uint64_t
Syscall_SysCtl(uint64_t user_node,uint64_t user_oldval,uint64_t user_newval)574 Syscall_SysCtl(uint64_t user_node, uint64_t user_oldval, uint64_t user_newval)
575 {
576 uint64_t status;
577 char node[64];
578
579 status = Copy_StrIn(user_node, &node, sizeof(node));
580 if (status != 0) {
581 return SYSCALL_PACK(status, 0);
582 }
583
584 uint64_t scType = SysCtl_GetType(node);
585 if (scType == SYSCTL_TYPE_INVALID) {
586 return SYSCALL_PACK(ENOENT, 0);
587 }
588
589 if (user_oldval != 0) {
590 switch (scType) {
591 case SYSCTL_TYPE_STR: {
592 SysCtlString *scStr = SysCtl_GetObject(node);
593 status = Copy_Out(scStr, user_oldval, sizeof(*scStr));
594 break;
595 }
596 case SYSCTL_TYPE_INT: {
597 SysCtlInt *scInt = SysCtl_GetObject(node);
598 status = Copy_Out(scInt, user_oldval, sizeof(*scInt));
599 break;
600 }
601 case SYSCTL_TYPE_BOOL: {
602 SysCtlBool *scBool = SysCtl_GetObject(node);
603 status = Copy_Out(scBool, user_oldval, sizeof(scBool));
604 break;
605 }
606 default: {
607 status = EINVAL;
608 }
609 }
610
611 if (status != 0) {
612 return SYSCALL_PACK(status, 0);
613 }
614 }
615
616 if (user_newval != 0) {
617 switch (scType) {
618 case SYSCTL_TYPE_STR: {
619 SysCtlString scStr;
620 status = Copy_In(user_newval, &scStr, sizeof(scStr));
621 if (status != 0) {
622 return SYSCALL_PACK(status, 0);
623 }
624 status = SysCtl_SetObject(node, (void *)&scStr);
625 break;
626 }
627 case SYSCTL_TYPE_INT: {
628 SysCtlInt scInt;
629 status = Copy_In(user_newval, &scInt, sizeof(scInt));
630 if (status != 0) {
631 return SYSCALL_PACK(status, 0);
632 }
633 status = SysCtl_SetObject(node, (void *)&scInt);
634 break;
635 }
636 case SYSCTL_TYPE_BOOL: {
637 SysCtlBool scBool;
638 status = Copy_In(user_newval, &scBool, sizeof(scBool));
639 if (status != 0) {
640 return SYSCALL_PACK(status, 0);
641 }
642 status = SysCtl_SetObject(node, (void *)&scBool);
643 break;
644 }
645 default: {
646 status = EINVAL;
647 }
648 }
649 }
650
651 return SYSCALL_PACK(status, 0);
652 }
653
654 uint64_t
Syscall_FSMount(uint64_t user_mntpt,uint64_t user_device,uint64_t flags)655 Syscall_FSMount(uint64_t user_mntpt, uint64_t user_device, uint64_t flags)
656 {
657 return SYSCALL_PACK(ENOSYS, 0);
658 }
659
660 uint64_t
Syscall_FSUnmount(uint64_t user_mntpt)661 Syscall_FSUnmount(uint64_t user_mntpt)
662 {
663 return SYSCALL_PACK(ENOSYS, 0);
664 }
665
666 uint64_t
Syscall_FSInfo(uint64_t user_fsinfo,uint64_t max)667 Syscall_FSInfo(uint64_t user_fsinfo, uint64_t max)
668 {
669 return SYSCALL_PACK(ENOSYS, 0);
670 }
671
672 uint64_t
Syscall_Entry(uint64_t syscall,uint64_t a1,uint64_t a2,uint64_t a3,uint64_t a4,uint64_t a5)673 Syscall_Entry(uint64_t syscall, uint64_t a1, uint64_t a2,
674 uint64_t a3, uint64_t a4, uint64_t a5)
675 {
676 switch (syscall)
677 {
678 case SYSCALL_NULL:
679 return 0;
680 case SYSCALL_TIME:
681 return Syscall_Time();
682 case SYSCALL_GETPID:
683 return Syscall_GetPID();
684 case SYSCALL_EXIT:
685 Syscall_Exit(a1);
686 return 0; // To eliminate warning
687 case SYSCALL_SPAWN:
688 return Syscall_Spawn(a1, a2);
689 case SYSCALL_WAIT:
690 return Syscall_Wait(a1);
691 case SYSCALL_MMAP:
692 return Syscall_MMap(a1, a2, a3);
693 case SYSCALL_MUNMAP:
694 return Syscall_MUnmap(a1, a2);
695 case SYSCALL_MPROTECT:
696 return Syscall_MProtect(a1, a2, a3);
697 case SYSCALL_READ:
698 return Syscall_Read(a1, a2, a3, a4);
699 case SYSCALL_WRITE:
700 return Syscall_Write(a1, a2, a3, a4);
701 case SYSCALL_FLUSH:
702 return Syscall_Flush(a1);
703 case SYSCALL_OPEN:
704 return Syscall_Open(a1, a2);
705 case SYSCALL_CLOSE:
706 return Syscall_Close(a1);
707 case SYSCALL_STAT:
708 return Syscall_Stat(a1, a2);
709 case SYSCALL_READDIR:
710 return Syscall_ReadDir(a1, (char *)a2, a3, a4);
711 case SYSCALL_THREADCREATE:
712 return Syscall_ThreadCreate(a1, a2);
713 case SYSCALL_GETTID:
714 return Syscall_GetTID();
715 case SYSCALL_THREADEXIT:
716 Syscall_ThreadExit(a1);
717 return 0;
718 case SYSCALL_THREADSLEEP:
719 return Syscall_ThreadSleep(a1);
720 case SYSCALL_THREADWAIT:
721 return Syscall_ThreadWait(a1);
722 case SYSCALL_NICSTAT:
723 return Syscall_NICStat(a1, a2);
724 case SYSCALL_NICSEND:
725 return Syscall_NICSend(a1, a2);
726 case SYSCALL_NICRECV:
727 return Syscall_NICRecv(a1, a2);
728 case SYSCALL_SYSCTL:
729 return Syscall_SysCtl(a1, a2, a3);
730 case SYSCALL_FSMOUNT:
731 return Syscall_FSMount(a1, a2, a3);
732 case SYSCALL_FSUNMOUNT:
733 return Syscall_FSUnmount(a1);
734 case SYSCALL_FSINFO:
735 return Syscall_FSInfo(a1, a2);
736 default:
737 return SYSCALL_PACK(ENOSYS, 0);
738 }
739 }
740
741