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