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