1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/device/timer.h"
4#include "mos/ipc/ipc_io.h"
5#include "mos/ipc/memfd.h"
6#include "mos/ipc/pipe.h"
7#include "mos/misc/power.h"
8#include "mos/mm/dma.h"
9#include "mos/tasks/signal.h"
10
11#include <bits/posix/iovec.h>
12#include <errno.h>
13#include <fcntl.h>
14#include <mos/filesystem/fs_types.h>
15#include <mos/filesystem/vfs.h>
16#include <mos/io/io.h>
17#include <mos/locks/futex.h>
18#include <mos/mm/mmap.h>
19#include <mos/mm/paging/paging.h>
20#include <mos/platform/platform.h>
21#include <mos/syscall/decl.h>
22#include <mos/syscall/number.h>
23#include <mos/syslog/printk.h>
24#include <mos/tasks/elf.h>
25#include <mos/tasks/process.h>
26#include <mos/tasks/schedule.h>
27#include <mos/tasks/task_types.h>
28#include <mos/tasks/thread.h>
29#include <mos/types.h>
30#include <mos_stdlib.h>
31#include <mos_string.h>
32#include <sys/poll.h>
33
34#define DEFINE_SYSCALL(ret, name) \
35 MOS_STATIC_ASSERT(SYSCALL_DEFINED(name)); \
36 ret define_syscall(name)
37
38DEFINE_SYSCALL(void, poweroff)(bool reboot, u32 magic)
39{
40#define POWEROFF_MAGIC MOS_FOURCC('G', 'B', 'y', 'e')
41 if (magic != POWEROFF_MAGIC)
42 {
43 mos_warn("poweroff syscall called with wrong magic number (0x%x)", magic);
44 return;
45 }
46
47 if (!reboot)
48 {
49 pr_info("Meow, see ya~ :3");
50 power_shutdown();
51 }
52 else
53 {
54 mos_warn("reboot is not implemented yet");
55 }
56}
57
58DEFINE_SYSCALL(fd_t, vfs_openat)(fd_t dirfd, const char *path, open_flags flags)
59{
60 if (path == NULL)
61 return -1;
62
63 file_t *f = vfs_openat(fd: dirfd, path, flags);
64 if (IS_ERR(ptr: f))
65 return PTR_ERR(ptr: f);
66 return process_attach_ref_fd(current_process, file: &f->io, flags: FD_FLAGS_NONE);
67}
68
69DEFINE_SYSCALL(long, vfs_fstatat)(fd_t fd, const char *path, file_stat_t *stat_buf, fstatat_flags flags)
70{
71 return vfs_fstatat(fd, path, stat: stat_buf, flags);
72}
73
74DEFINE_SYSCALL(size_t, io_read)(fd_t fd, void *buf, size_t count)
75{
76 if (buf == NULL)
77 return -EFAULT;
78
79 io_t *io = process_get_fd(current_process, fd);
80 if (!io)
81 return -EBADF;
82
83 return io_read(io, buf, count);
84}
85
86DEFINE_SYSCALL(size_t, io_write)(fd_t fd, const void *buf, size_t count)
87{
88 if (buf == NULL)
89 {
90 pr_warn("io_write called with invalid arguments (fd=%d, buf=%p, count=%zd)", fd, buf, count);
91 return -EFAULT;
92 }
93
94 io_t *io = process_get_fd(current_process, fd);
95 if (!io)
96 {
97 pr_warn("io_write called with invalid fd %d", fd);
98 return -EBADF;
99 }
100
101 return io_write(io, buf, count);
102}
103
104DEFINE_SYSCALL(bool, io_close)(fd_t fd)
105{
106 process_detach_fd(current_process, fd);
107 return true;
108}
109
110DEFINE_SYSCALL([[noreturn]] void, exit)(u32 exit_code)
111{
112 // only use the lower 8 bits
113 exit_code &= 0xff;
114 process_exit(current_process, exit_code, signal: 0);
115}
116
117DEFINE_SYSCALL(void, yield_cpu)(void)
118{
119 spinlock_acquire(&current_thread->state_lock);
120 reschedule();
121}
122
123DEFINE_SYSCALL(pid_t, fork)(void)
124{
125 process_t *parent = current_process;
126 process_t *child = process_do_fork(process: parent);
127 if (unlikely(child == NULL))
128 return -1;
129 return child->pid; // return 0 for child, pid for parent
130}
131
132DEFINE_SYSCALL(pid_t, get_pid)(void)
133{
134 return current_process->pid;
135}
136
137DEFINE_SYSCALL(pid_t, get_parent_pid)(void)
138{
139 return current_process->parent->pid;
140}
141
142DEFINE_SYSCALL(pid_t, spawn)(const char *path, const char *const argv[], const char *const envp[])
143{
144 const stdio_t stdio = current_stdio();
145 process_t *process = elf_create_process(path, current_process, argv, envp, ios: &stdio);
146
147 if (process == NULL)
148 return -1;
149
150 return process->pid;
151}
152
153DEFINE_SYSCALL(tid_t, create_thread)(const char *name, thread_entry_t entry, void *arg, size_t stack_size, void *stack)
154{
155 thread_t *thread = thread_new(current_process, mode: THREAD_MODE_USER, name, stack_size, stack);
156 if (thread == NULL)
157 return -1;
158
159 platform_context_setup_child_thread(thread, entry, arg);
160 thread_complete_init(thread);
161 scheduler_add_thread(thread);
162 return thread->tid;
163}
164
165DEFINE_SYSCALL(tid_t, get_tid)(void)
166{
167 return current_thread->tid;
168}
169
170DEFINE_SYSCALL([[noreturn]] void, thread_exit)(void)
171{
172 thread_exit(current_thread);
173}
174
175DEFINE_SYSCALL(bool, wait_for_thread)(tid_t tid)
176{
177 return thread_wait_for_tid(tid);
178}
179
180DEFINE_SYSCALL(bool, futex_wait)(futex_word_t *futex, u32 val)
181{
182 return futex_wait(futex, expected: val);
183}
184
185DEFINE_SYSCALL(bool, futex_wake)(futex_word_t *futex, size_t count)
186{
187 return futex_wake(lock: futex, num_to_wake: count);
188}
189
190DEFINE_SYSCALL(fd_t, ipc_create)(const char *name, size_t max_pending_connections)
191{
192 io_t *io = ipc_create(name, max_pending_connections);
193 if (IS_ERR(ptr: io))
194 return PTR_ERR(ptr: io);
195 return process_attach_ref_fd(current_process, file: io, flags: FD_FLAGS_NONE);
196}
197
198DEFINE_SYSCALL(fd_t, ipc_accept)(fd_t listen_fd)
199{
200 io_t *server = process_get_fd(current_process, fd: listen_fd);
201 if (server == NULL)
202 return -1;
203
204 io_t *client_io = ipc_accept(server);
205 if (IS_ERR(ptr: client_io))
206 return PTR_ERR(ptr: client_io);
207
208 return process_attach_ref_fd(current_process, file: client_io, flags: FD_FLAGS_NONE);
209}
210
211DEFINE_SYSCALL(fd_t, ipc_connect)(const char *server, size_t buffer_size)
212{
213 io_t *io = ipc_connect(name: server, buffer_size);
214 if (IS_ERR(ptr: io))
215 return PTR_ERR(ptr: io);
216 return process_attach_ref_fd(current_process, file: io, flags: FD_FLAGS_NONE);
217}
218
219DEFINE_SYSCALL(u64, arch_syscall)(u64 syscall, u64 arg1, u64 arg2, u64 arg3, u64 arg4)
220{
221 return platform_arch_syscall(syscall, arg1, arg2, arg3, arg4);
222}
223
224DEFINE_SYSCALL(long, vfs_mount)(const char *device, const char *mountpoint, const char *fs_type, const char *options)
225{
226 return vfs_mount(device, path: mountpoint, fs: fs_type, options);
227}
228
229DEFINE_SYSCALL(ssize_t, vfs_readlinkat)(fd_t dirfd, const char *path, char *buf, size_t buflen)
230{
231 return vfs_readlinkat(dirfd, path, buf, size: buflen);
232}
233
234DEFINE_SYSCALL(long, vfs_unlinkat)(fd_t dirfd, const char *path)
235{
236 return vfs_unlinkat(dirfd, path);
237}
238
239DEFINE_SYSCALL(long, vfs_symlink)(const char *target, const char *linkpath)
240{
241 return vfs_symlink(path: target, target: linkpath);
242}
243
244DEFINE_SYSCALL(long, vfs_mkdir)(const char *path)
245{
246 return vfs_mkdir(path);
247}
248
249DEFINE_SYSCALL(size_t, vfs_list_dir)(fd_t fd, char *buffer, size_t buffer_size)
250{
251 io_t *io = process_get_fd(current_process, fd);
252 if (io == NULL)
253 return false;
254 return vfs_list_dir(io, buf: buffer, size: buffer_size);
255}
256
257DEFINE_SYSCALL(long, fd_manipulate)(fd_t fd, u64 op, void *arg)
258{
259 fd_type *fdt = &current_process->files[fd];
260 if (fdt->io == NULL)
261 return -EBADF;
262
263 switch (op)
264 {
265 case F_DUPFD:
266 {
267 fd_t fd2 = process_attach_ref_fd(current_process, file: fdt->io, flags: fdt->flags);
268 return fd2;
269 }
270 case F_DUPFD_CLOEXEC:
271 {
272 fd_t fd2 = process_attach_ref_fd(current_process, file: fdt->io, flags: fdt->flags | FD_FLAGS_CLOEXEC);
273 return fd2;
274 }
275 case F_GETFD:
276 {
277 return fdt->flags;
278 }
279 case F_SETFD:
280 {
281 // test if arg is a valid flag
282 u64 flags = (u64) arg;
283 if (flags & ~FD_FLAGS_CLOEXEC)
284 return -EINVAL;
285 fdt->flags = flags;
286 return 0;
287 }
288 case F_GETFL:
289 case F_SETFL:
290 {
291 return -ENOSYS; // not implemented
292 }
293 case F_GETLK:
294 case F_SETLK:
295 case F_SETLKW:
296 {
297 return -ENOSYS; // not implemented
298 }
299 case F_GETOWN:
300 case F_SETOWN:
301 case F_GETOWN_EX:
302 case F_SETOWN_EX:
303 case F_GETSIG:
304 case F_SETSIG:
305 case F_GETOWNER_UIDS:
306 case F_ADD_SEALS:
307 case F_GET_SEALS:
308 {
309 return -ENOSYS; // not implemented
310 }
311 }
312
313 return -EINVAL;
314}
315
316DEFINE_SYSCALL(void *, mmap_anonymous)(ptr_t hint_addr, size_t size, mem_perm_t perm, mmap_flags_t flags)
317{
318 const vm_flags vmflags = VM_USER | (vm_flags) perm; // vm_flags shares the same values as mem_perm_t
319 const size_t n_pages = ALIGN_UP_TO_PAGE(size) / MOS_PAGE_SIZE;
320
321 ptr_t result = mmap_anonymous(current_mm, hint_addr, flags, vm_flags: vmflags, n_pages);
322 return (void *) result;
323}
324
325DEFINE_SYSCALL(void *, mmap_file)(ptr_t hint_addr, size_t size, mem_perm_t perm, mmap_flags_t mmap_flags, fd_t fd, off_t offset)
326{
327 const vm_flags vmflags = VM_USER | (vm_flags) perm; // vm_flags shares the same values as mem_perm_t
328 const size_t n_pages = ALIGN_UP_TO_PAGE(size) / MOS_PAGE_SIZE;
329
330 io_t *io = process_get_fd(current_process, fd);
331 if (io == NULL)
332 return NULL;
333
334 ptr_t result = mmap_file(current_mm, hint_addr, flags: mmap_flags, vm_flags: vmflags, n_pages, io, offset);
335 return (void *) result;
336}
337
338DEFINE_SYSCALL(pid_t, wait_for_process)(pid_t pid, u32 *exit_code, u32 flags)
339{
340 return process_wait_for_pid(pid, exit_code, flags);
341}
342
343DEFINE_SYSCALL(bool, munmap)(void *addr, size_t size)
344{
345 return munmap(addr: (ptr_t) addr, size);
346}
347
348DEFINE_SYSCALL(long, vfs_chdirat)(fd_t dirfd, const char *path)
349{
350 return vfs_chdirat(dirfd, path);
351}
352
353DEFINE_SYSCALL(ssize_t, vfs_getcwd)(char *buf, size_t size)
354{
355 return vfs_getcwd(buf, size);
356}
357
358DEFINE_SYSCALL(off_t, io_seek)(fd_t fd, off_t offset, io_seek_whence_t whence)
359{
360 io_t *io = process_get_fd(current_process, fd);
361 if (io == NULL)
362 return -1;
363 return io_seek(io, offset, whence);
364}
365
366DEFINE_SYSCALL(off_t, io_tell)(fd_t fd)
367{
368 io_t *io = process_get_fd(current_process, fd);
369 if (io == NULL)
370 return -1;
371 return io_tell(io);
372}
373
374DEFINE_SYSCALL(bool, signal_register)(signal_t sig, const sigaction_t *action)
375{
376 return process_register_signal_handler(current_process, sig, sigaction: action);
377}
378
379DEFINE_SYSCALL(long, signal_process)(pid_t pid, signal_t sig)
380{
381 process_t *process = process_get(pid);
382 if (!process)
383 return -ESRCH;
384 return signal_send_to_process(target: process, signal: sig);
385}
386
387DEFINE_SYSCALL(long, signal_thread)(tid_t tid, signal_t sig)
388{
389 thread_t *thread = thread_get(id: tid);
390 if (thread == NULL)
391 return -ESRCH;
392 return signal_send_to_thread(target: thread, signal: sig);
393}
394
395DEFINE_SYSCALL([[noreturn]] void, signal_return)(void *sp)
396{
397 platform_restore_from_signal_handler(sp);
398}
399
400DEFINE_SYSCALL(bool, vm_protect)(void *addr, size_t size, mem_perm_t perm)
401{
402 return vm_protect(current_mm, addr: (ptr_t) addr, size, perm: (vm_flags) perm);
403}
404
405DEFINE_SYSCALL(int, io_poll)(struct pollfd *fds, nfds_t nfds, int timeout)
406{
407 if (timeout == 0) // poll with timeout 0 is just a check
408 return 0;
409
410 if (!fds || nfds == 0)
411 return -1;
412
413 for (nfds_t i = 0; i < nfds; i++)
414 {
415 if (fds[i].fd < 0)
416 fds[i].revents = 0;
417 pr_info2("io_poll: fd=%d, events=%d", fds[i].fd, fds[i].events);
418 }
419
420 pr_emerg("io_poll is not implemented yet\n");
421 signal_send_to_thread(current_thread, SIGKILL); // unimplemented
422 return 0;
423}
424
425#ifndef FD_CLR
426#define FD_CLR(__fd, __set) (__set->fds_bits[__fd / 8] &= ~(1 << (__fd % 8)))
427#endif
428
429#ifndef FD_ISSET
430#define FD_ISSET(__fd, __set) (__set->fds_bits[__fd / 8] & (1 << (__fd % 8)))
431#endif
432
433#ifndef FD_SET
434#define FD_SET(__fd, __set) (__set->fds_bits[__fd / 8] |= 1 << (__fd % 8))
435#endif
436
437#ifndef FD_ZERO
438#define FD_ZERO(__set) memset(__set->fds_bits, 0, sizeof(fd_set))
439#endif
440
441DEFINE_SYSCALL(int, io_pselect)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)
442{
443 MOS_UNUSED(timeout);
444 MOS_UNUSED(sigmask);
445
446 for (int i = 0; i < nfds; i++)
447 {
448 if (readfds && FD_ISSET(i, readfds))
449 {
450 // pr_info2("io_pselect: fd=%d, read", i);
451 }
452 if (writefds && FD_ISSET(i, writefds))
453 {
454 // pr_info2("io_pselect: fd=%d, write", i);
455 }
456 if (exceptfds && FD_ISSET(i, exceptfds))
457 {
458 // pr_info2("io_pselect: fd=%d, except", i);
459 }
460 }
461
462 return 1; // stub
463}
464
465DEFINE_SYSCALL(long, execveat)(fd_t dirfd, const char *path, const char *const argv[], const char *const envp[], u32 flags)
466{
467 return process_do_execveat(current_process, dirfd, path, argv, envp, flags);
468}
469
470DEFINE_SYSCALL(long, clock_msleep)(u64 ms)
471{
472 timer_msleep(ms);
473 return 0;
474}
475
476DEFINE_SYSCALL(fd_t, io_dup)(fd_t fd)
477{
478 io_t *io = process_get_fd(current_process, fd);
479 if (io == NULL)
480 return -EBADF; // fd is not a valid file descriptor
481 return process_attach_ref_fd(current_process, file: io_ref(io), current_process->files[fd].flags);
482}
483
484DEFINE_SYSCALL(fd_t, io_dup2)(fd_t oldfd, fd_t newfd)
485{
486 fd_type *old = &current_process->files[oldfd];
487 if (old->io == NULL)
488 return -EBADF; // oldfd is not a valid file descriptor
489
490 if (oldfd == newfd)
491 return newfd;
492
493 process_detach_fd(current_process, fd: newfd);
494
495 current_process->files[newfd].io = io_ref(io: old->io);
496 current_process->files[newfd].flags = old->flags;
497 return newfd;
498}
499
500DEFINE_SYSCALL(bool, dmabuf_alloc)(size_t n_pages, ptr_t *phys, ptr_t *virt)
501{
502 pfn_t pfn = dmabuf_allocate(n_pages, pages: virt);
503 *phys = pfn * MOS_PAGE_SIZE;
504 return !IS_ERR_VALUE(pfn);
505}
506
507DEFINE_SYSCALL(bool, dmabuf_free)(ptr_t vaddr, ptr_t paddr)
508{
509 return dmabuf_free(vaddr, paddr);
510}
511
512DEFINE_SYSCALL(bool, dmabuf_share)(void *buffer, size_t size, ptr_t *phyaddr)
513{
514 pfn_t pfn = dmabuf_share(buffer, size);
515
516 if (IS_ERR_VALUE(pfn))
517 return false;
518
519 *phyaddr = pfn * MOS_PAGE_SIZE;
520 return true;
521}
522
523DEFINE_SYSCALL(bool, dmabuf_unshare)(ptr_t phys, size_t size, void *buf)
524{
525 return dmabuf_unshare(phys, size, virt: buf);
526}
527
528DEFINE_SYSCALL(long, pipe)(fd_t *reader, fd_t *writer, fd_flags_t flags)
529{
530 pipe_t *pipe = pipe_create(MOS_PAGE_SIZE * 4);
531 if (IS_ERR(ptr: pipe))
532 return PTR_ERR(ptr: pipe);
533
534 pipeio_t *pipeio = pipeio_create(pipe);
535 *reader = process_attach_ref_fd(current_process, file: &pipeio->io_r, flags);
536 *writer = process_attach_ref_fd(current_process, file: &pipeio->io_w, flags);
537 return 0;
538}
539
540DEFINE_SYSCALL(ssize_t, io_readv)(fd_t fd, const struct iovec *iov, int iovcnt)
541{
542 if (fd < 0)
543 return -EBADF;
544
545 if (iov == NULL)
546 return -EFAULT;
547
548 io_t *io = process_get_fd(current_process, fd);
549 if (!io)
550 return -EBADF;
551
552 for (int i = 0; i < iovcnt; i++)
553 {
554 if (iov[i].iov_base == NULL)
555 return -EFAULT;
556 }
557
558 ssize_t bytes_read = 0;
559
560 for (int i = 0; i < iovcnt; i++)
561 {
562 size_t ret = io_read(io, buf: iov[i].iov_base, count: iov[i].iov_len);
563 if (IS_ERR_VALUE(ret))
564 return ret;
565
566 bytes_read += ret;
567
568 if (ret != iov[i].iov_len)
569 break; // short read, leave
570 }
571
572 return bytes_read;
573}
574
575DEFINE_SYSCALL(long, vfs_unmount)(const char *path)
576{
577 return vfs_unmount(path);
578}
579
580DEFINE_SYSCALL(long, clock_gettimeofday)(struct timespec *ts)
581{
582 timeval_t tv;
583 platform_get_time(val: &tv);
584 ts->tv_sec = tv.hour * 3600 + tv.minute * 60 + tv.second;
585 ts->tv_nsec = 0;
586 return 0;
587}
588
589DEFINE_SYSCALL(long, thread_setname)(tid_t tid, const char *name)
590{
591 thread_t *thread = thread_get(id: tid);
592 if (thread == NULL)
593 return -ESRCH;
594
595 if (thread->name)
596 kfree(ptr: thread->name);
597
598 thread->name = strdup(src: name);
599 return true;
600}
601
602DEFINE_SYSCALL(ssize_t, thread_getname)(tid_t tid, char *buf, size_t buflen)
603{
604 thread_t *thread = thread_get(id: tid);
605
606 if (thread == NULL)
607 return -ESRCH;
608
609 char *end = strncpy(dest: buf, src: thread->name, n: buflen);
610 return end - buf;
611}
612
613DEFINE_SYSCALL(long, vfs_fchmodat)(fd_t dirfd, const char *path, int mode, int flags)
614{
615 return vfs_fchmodat(fd: dirfd, path, perm: mode, flags);
616}
617
618DEFINE_SYSCALL(long, io_pread)(fd_t fd, void *buf, size_t count, off_t offset)
619{
620 if (fd < 0)
621 return -EBADF;
622
623 if (buf == NULL)
624 return -EFAULT;
625
626 io_t *io = process_get_fd(current_process, fd);
627 if (!io)
628 return -EBADF;
629
630 return io_pread(io, buf, count, offset);
631}
632
633DEFINE_SYSCALL(fd_t, memfd_create)(const char *name, u32 flags)
634{
635 io_t *io = memfd_create(name);
636 if (IS_ERR(ptr: io))
637 return PTR_ERR(ptr: io);
638
639 return process_attach_ref_fd(current_process, file: io, flags);
640}
641
642DEFINE_SYSCALL(long, signal_mask_op)(int how, const sigset_t *set, sigset_t *oldset)
643{
644 if (oldset)
645 *oldset = current_thread->signal_info.mask;
646
647 if (set)
648 {
649 switch (how)
650 {
651 case SIG_SETMASK: current_thread->signal_info.mask = *set; break;
652 case SIG_BLOCK:
653 {
654 char *ptr = (char *) set;
655 char *mask = (char *) &current_thread->signal_info.mask;
656 for (size_t i = 0; i < sizeof(sigset_t); i++)
657 mask[i] |= ptr[i];
658 break;
659 }
660 case SIG_UNBLOCK:
661 {
662 char *ptr = (char *) set;
663 char *mask = (char *) &current_thread->signal_info.mask;
664 for (size_t i = 0; i < sizeof(sigset_t); i++)
665 mask[i] &= ~ptr[i];
666 break;
667 }
668 default: return -EINVAL;
669 }
670 }
671
672 return 0;
673}
674
675DEFINE_SYSCALL(long, vfs_fsync)(fd_t fd, bool data_only)
676{
677 io_t *io = process_get_fd(current_process, fd);
678 if (!io)
679 return -EBADF;
680
681 if (io->type != IO_FILE)
682 return -EBADF;
683
684 return vfs_fsync(io, sync_metadata: data_only, start: 0, end: (off_t) -1);
685}
686