aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/sysdeps/managarm
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-03-07 17:28:00 -0500
committerIan Moffett <ian@osmora.org>2024-03-07 17:28:32 -0500
commitbd5969fc876a10b18613302db7087ef3c40f18e1 (patch)
tree7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/sysdeps/managarm
parenta95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff)
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/sysdeps/managarm')
-rw-r--r--lib/mlibc/sysdeps/managarm/aarch64/crt-src/Scrt1.S10
-rw-r--r--lib/mlibc/sysdeps/managarm/aarch64/crt-src/crt0.S11
-rw-r--r--lib/mlibc/sysdeps/managarm/aarch64/crt-src/crti.S15
-rw-r--r--lib/mlibc/sysdeps/managarm/aarch64/crt-src/crtn.S11
-rw-r--r--lib/mlibc/sysdeps/managarm/aarch64/signals.S10
-rw-r--r--lib/mlibc/sysdeps/managarm/aarch64/thread.cpp56
-rw-r--r--lib/mlibc/sysdeps/managarm/aarch64/thread_entry.S11
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/drm.cpp1176
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/ensure.cpp38
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/entry.cpp132
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/file.cpp2526
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/fork-exec.cpp744
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/ioctl.cpp708
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/memory.cpp30
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/mount.cpp44
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/net.cpp57
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/sched.cpp102
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/signals.cpp139
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/socket.cpp423
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/time.cpp81
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/access.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/auxv.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/blkcnt_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/blksize_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/clockid_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/dev_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/epoll.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/errno.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/fcntl.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/fsblkcnt_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/fsfilcnt_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/gid_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/in.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/ino_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/inotify.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/ioctls.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/limits.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/mode_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/mqueue.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/msg.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/nlink_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/packet.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/pid_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/poll.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/ptrace.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/reboot.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/resource.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/seek-whence.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/shm.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/signal.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/socket.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/socklen_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/stat.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/statfs.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/statvfs.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/suseconds_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/termios.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/time.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/uid_t.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/utsname.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/vm-flags.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/vt.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/wait.h1
l---------lib/mlibc/sysdeps/managarm/include/abi-bits/xattr.h1
-rw-r--r--lib/mlibc/sysdeps/managarm/include/mlibc/posix-pipe.hpp300
-rw-r--r--lib/mlibc/sysdeps/managarm/include/mlibc/thread-entry.hpp10
-rw-r--r--lib/mlibc/sysdeps/managarm/meson.build145
-rw-r--r--lib/mlibc/sysdeps/managarm/rtdl-generic/support.cpp444
-rw-r--r--lib/mlibc/sysdeps/managarm/x86_64/crt-src/Scrt1.S8
-rw-r--r--lib/mlibc/sysdeps/managarm/x86_64/crt-src/crt0.S10
-rw-r--r--lib/mlibc/sysdeps/managarm/x86_64/crt-src/crti.S15
-rw-r--r--lib/mlibc/sysdeps/managarm/x86_64/crt-src/crtn.S11
-rw-r--r--lib/mlibc/sysdeps/managarm/x86_64/signals.S10
-rw-r--r--lib/mlibc/sysdeps/managarm/x86_64/thread.cpp56
-rw-r--r--lib/mlibc/sysdeps/managarm/x86_64/thread_entry.S11
75 files changed, 7388 insertions, 0 deletions
diff --git a/lib/mlibc/sysdeps/managarm/aarch64/crt-src/Scrt1.S b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/Scrt1.S
new file mode 100644
index 0000000..3e56608
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/Scrt1.S
@@ -0,0 +1,10 @@
+.section .text
+.global _start
+_start:
+ mov x0, sp
+ adr x1, main
+
+ bl __mlibc_entry
+ brk #0
+
+.section .note.GNU-stack,"",%progbits
diff --git a/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crt0.S b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crt0.S
new file mode 100644
index 0000000..5e59593
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crt0.S
@@ -0,0 +1,11 @@
+
+.section .text
+.global _start
+_start:
+ mov x0, sp
+ adrp x1, main
+ add x1, x1, :lo12:main
+ bl __mlibc_entry
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crti.S b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crti.S
new file mode 100644
index 0000000..4307dfb
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crti.S
@@ -0,0 +1,15 @@
+ .ident "aarch64-managarm-mlibc crti"
+
+ .section .init
+ .global _init
+_init:
+ stp x29, x30, [sp, -16]!
+ mov x29, sp
+
+ .section .fini
+ .global _fini
+_fini:
+ stp x29, x30, [sp, -16]!
+ mov x29, sp
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crtn.S b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crtn.S
new file mode 100644
index 0000000..005d870
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/aarch64/crt-src/crtn.S
@@ -0,0 +1,11 @@
+ .ident "aarch64-managarm-mlibc crtn"
+
+ .section .init
+ ldp x29, x30, [sp], #16
+ ret
+
+ .section .fini
+ ldp x29, x30, [sp], #16
+ ret
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/lib/mlibc/sysdeps/managarm/aarch64/signals.S b/lib/mlibc/sysdeps/managarm/aarch64/signals.S
new file mode 100644
index 0000000..044767d
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/aarch64/signals.S
@@ -0,0 +1,10 @@
+
+.section .text
+.global __mlibc_signal_restore
+__mlibc_signal_restore:
+ ldr x0, =0x80000006
+ svc 0
+ brk #1
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/sysdeps/managarm/aarch64/thread.cpp b/lib/mlibc/sysdeps/managarm/aarch64/thread.cpp
new file mode 100644
index 0000000..88f1bf3
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/aarch64/thread.cpp
@@ -0,0 +1,56 @@
+#include <mlibc/thread-entry.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/tcb.hpp>
+#include <bits/ensure.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stddef.h>
+
+extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) {
+ // Wait until our parent sets up the TID.
+ while(!__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED))
+ mlibc::sys_futex_wait(&tcb->tid, 0, nullptr);
+
+ if(mlibc::sys_tcb_set(tcb))
+ __ensure(!"sys_tcb_set() failed");
+
+ tcb->invokeThreadFunc(entry, user_arg);
+
+ auto self = reinterpret_cast<Tcb *>(tcb);
+
+ __atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE);
+ mlibc::sys_futex_wake(&self->didExit);
+
+ mlibc::sys_thread_exit();
+}
+
+namespace mlibc {
+
+static constexpr size_t default_stacksize = 0x200000;
+
+int sys_prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, size_t *guard_size, void **stack_base) {
+ if (!*stack_size)
+ *stack_size = default_stacksize;
+ *guard_size = 0;
+
+ if (*stack) {
+ *stack_base = *stack;
+ } else {
+ *stack_base = mmap(nullptr, *stack_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if(*stack_base == MAP_FAILED) {
+ return errno;
+ }
+ }
+
+ uintptr_t *sp = reinterpret_cast<uintptr_t *>(reinterpret_cast<uintptr_t>(*stack_base) + *stack_size);
+
+ *--sp = reinterpret_cast<uintptr_t>(tcb);
+ *--sp = reinterpret_cast<uintptr_t>(user_arg);
+ *--sp = reinterpret_cast<uintptr_t>(entry);
+ *stack = reinterpret_cast<void*>(sp);
+ return 0;
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/aarch64/thread_entry.S b/lib/mlibc/sysdeps/managarm/aarch64/thread_entry.S
new file mode 100644
index 0000000..4cfcb4c
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/aarch64/thread_entry.S
@@ -0,0 +1,11 @@
+
+.section .text
+.global __mlibc_start_thread
+__mlibc_start_thread:
+ ldp x0, x1, [sp]
+ ldr x2, [sp, #16]
+ add sp, sp, #24
+ bl __mlibc_enter_thread
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/sysdeps/managarm/generic/drm.cpp b/lib/mlibc/sysdeps/managarm/generic/drm.cpp
new file mode 100644
index 0000000..805c366
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/drm.cpp
@@ -0,0 +1,1176 @@
+#include <drm/drm_fourcc.h>
+#include <drm/drm.h>
+
+#include <bits/ensure.h>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-pipe.hpp>
+
+#include <fs.frigg_bragi.hpp>
+
+namespace mlibc {
+
+int ioctl_drm(int fd, unsigned long request, void *arg, int *result, HelHandle handle) {
+ managarm::fs::IoctlRequest<MemoryAllocator> ioctl_req(getSysdepsAllocator());
+
+ switch(request) {
+ case DRM_IOCTL_VERSION: {
+ auto param = reinterpret_cast<drm_version*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->version_major = resp.drm_version_major();
+ param->version_minor = resp.drm_version_minor();
+ param->version_patchlevel = resp.drm_version_patchlevel();
+
+ if(param->name)
+ memcpy(param->name, resp.drm_driver_name().data(), frg::min(param->name_len,
+ resp.drm_driver_name().size()));
+ if(param->date)
+ memcpy(param->date, resp.drm_driver_date().data(), frg::min(param->date_len,
+ resp.drm_driver_date().size()));
+ if(param->desc)
+ memcpy(param->desc, resp.drm_driver_desc().data(), frg::min(param->desc_len,
+ resp.drm_driver_desc().size()));
+
+ param->name_len = resp.drm_driver_name().size();
+ param->date_len = resp.drm_driver_date().size();
+ param->desc_len = resp.drm_driver_desc().size();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_GET_CAP: {
+ auto param = reinterpret_cast<drm_get_cap*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_capability(param->capability);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->value = resp.drm_value();
+ *result = resp.result();
+ return 0;
+ }
+ }
+ case DRM_IOCTL_SET_CLIENT_CAP: {
+ auto param = reinterpret_cast<drm_set_client_cap *>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: DRM_IOCTL_SET_CLIENT_CAP(" << param->capability << ") ignores its value\e[39m" << frg::endlog;
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_capability(param->capability);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->value = resp.drm_value();
+ *result = resp.result();
+ return 0;
+ }
+ }
+ case DRM_IOCTL_GET_MAGIC: {
+ auto param = reinterpret_cast<drm_auth *>(arg);
+ mlibc::infoLogger() << "\e[31mmlibc: DRM_IOCTL_GET_MAGIC is not implemented correctly\e[39m"
+ << frg::endlog;
+ param->magic = 1;
+ *result = 0;
+ return 0;
+ }
+ case DRM_IOCTL_AUTH_MAGIC: {
+ mlibc::infoLogger() << "\e[31mmlibc: DRM_IOCTL_AUTH_MAGIC is not implemented correctly\e[39m"
+ << frg::endlog;
+ *result = 0;
+ return 0;
+ }
+ case DRM_IOCTL_SET_MASTER: {
+ mlibc::infoLogger() << "\e[31mmlibc: DRM_IOCTL_SET_MASTER is not implemented correctly\e[39m"
+ << frg::endlog;
+ *result = 0;
+ return 0;
+ }
+ case DRM_IOCTL_DROP_MASTER: {
+ mlibc::infoLogger() << "\e[31mmlibc: DRM_IOCTL_DROP_MASTER is not implemented correctly\e[39m"
+ << frg::endlog;
+ *result = 0;
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETRESOURCES: {
+ auto param = reinterpret_cast<drm_mode_card_res *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ if(recv_resp.error() == kHelErrDismissed) {
+ return EINVAL;
+ }
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ for(size_t i = 0; i < resp.drm_fb_ids_size(); i++) {
+ if(i >= param->count_fbs)
+ break;
+ auto dest = reinterpret_cast<uint32_t *>(param->fb_id_ptr);
+ dest[i] = resp.drm_fb_ids(i);
+ }
+ param->count_fbs = resp.drm_fb_ids_size();
+
+ for(size_t i = 0; i < resp.drm_crtc_ids_size(); i++) {
+ if(i >= param->count_crtcs)
+ break;
+ auto dest = reinterpret_cast<uint32_t *>(param->crtc_id_ptr);
+ dest[i] = resp.drm_crtc_ids(i);
+ }
+ param->count_crtcs = resp.drm_crtc_ids_size();
+
+ for(size_t i = 0; i < resp.drm_connector_ids_size(); i++) {
+ if(i >= param->count_connectors)
+ break;
+ auto dest = reinterpret_cast<uint32_t *>(param->connector_id_ptr);
+ dest[i] = resp.drm_connector_ids(i);
+ }
+ param->count_connectors = resp.drm_connector_ids_size();
+
+ for(size_t i = 0; i < resp.drm_encoder_ids_size(); i++) {
+ if(i >= param->count_encoders)
+ continue;
+ auto dest = reinterpret_cast<uint32_t *>(param->encoder_id_ptr);
+ dest[i] = resp.drm_encoder_ids(i);
+ }
+ param->count_encoders = resp.drm_encoder_ids_size();
+
+ param->min_width = resp.drm_min_width();
+ param->max_width = resp.drm_max_width();
+ param->min_height = resp.drm_min_height();
+ param->max_height = resp.drm_max_height();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETCONNECTOR: {
+ auto param = reinterpret_cast<drm_mode_get_connector*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_connector_id(param->connector_id);
+ req.set_drm_max_modes(param->count_modes);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_list] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(reinterpret_cast<void *>(param->modes_ptr), param->count_modes * sizeof(drm_mode_modeinfo))
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ if(recv_resp.error() == kHelErrDismissed)
+ return EINVAL;
+
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_list.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ for(size_t i = 0; i < resp.drm_encoders_size(); i++) {
+ if(i >= param->count_encoders)
+ continue;
+ auto dest = reinterpret_cast<uint32_t *>(param->encoders_ptr);
+ dest[i] = resp.drm_encoders(i);
+ }
+
+ param->encoder_id = resp.drm_encoder_id();
+ param->connector_type = resp.drm_connector_type();
+ param->connector_type_id = resp.drm_connector_type_id();
+ param->connection = resp.drm_connection();
+ param->mm_width = resp.drm_mm_width();
+ param->mm_height = resp.drm_mm_height();
+ param->subpixel = resp.drm_subpixel();
+ param->pad = 0;
+ param->count_encoders = resp.drm_encoders_size();
+ param->count_modes = resp.drm_num_modes();
+
+ if(param->props_ptr) {
+ auto id_ptr = reinterpret_cast<uint32_t *>(param->props_ptr);
+ auto val_ptr = reinterpret_cast<uint64_t *>(param->prop_values_ptr);
+
+ for(size_t i = 0; i < frg::min(static_cast<size_t>(param->count_props), resp.drm_obj_property_ids_size()); i++) {
+ id_ptr[i] = resp.drm_obj_property_ids(i);
+ val_ptr[i] = resp.drm_obj_property_values(i);
+ }
+ }
+
+ param->count_props = resp.drm_obj_property_ids_size();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETPROPERTY: {
+ auto param = reinterpret_cast<drm_mode_get_property*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_property_id(param->prop_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() != managarm::fs::Errors::SUCCESS) {
+ mlibc::infoLogger() << "\e[31mmlibc: DRM_IOCTL_MODE_GETPROPERTY(" << param->prop_id << ") error " << (int) resp.error() << "\e[39m"
+ << frg::endlog;
+ *result = 0;
+ return EINVAL;
+ }
+
+ memcpy(param->name, resp.drm_property_name().data(), resp.drm_property_name().size());
+ param->count_values = resp.drm_property_vals_size();
+ param->flags = resp.drm_property_flags();
+
+ for(size_t i = 0; i < param->count_values && i < resp.drm_property_vals_size() && param->values_ptr; i++) {
+ auto dest = reinterpret_cast<uint64_t *>(param->values_ptr);
+ dest[i] = resp.drm_property_vals(i);
+ }
+
+ __ensure(resp.drm_enum_name_size() == resp.drm_enum_value_size());
+
+ for(size_t i = 0; i < param->count_enum_blobs && i < resp.drm_enum_name_size() && i < resp.drm_enum_value_size(); i++) {
+ auto dest = reinterpret_cast<drm_mode_property_enum *>(param->enum_blob_ptr);
+ dest[i].value = resp.drm_enum_value(i);
+ strncpy(dest[i].name, resp.drm_enum_name(i).data(), DRM_PROP_NAME_LEN);
+ }
+
+ param->count_enum_blobs = resp.drm_enum_name_size();
+
+ *result = 0;
+ return 0;
+ }
+ case DRM_IOCTL_MODE_SETPROPERTY: {
+ auto param = reinterpret_cast<drm_mode_connector_set_property *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_property_id(param->prop_id);
+ req.set_drm_property_value(param->value);
+ req.set_drm_obj_id(param->connector_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() != managarm::fs::Errors::SUCCESS) {
+ mlibc::infoLogger() << "\e[31mmlibc: DRM_IOCTL_MODE_SETPROPERTY(" << param->prop_id << ") error " << (int) resp.error() << "\e[39m"
+ << frg::endlog;
+ *result = 0;
+ return EINVAL;
+ }
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETPROPBLOB: {
+ auto param = reinterpret_cast<drm_mode_get_blob *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_blob_id(param->blob_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() != managarm::fs::Errors::SUCCESS) {
+ mlibc::infoLogger() << "\e[31mmlibc: DRM_IOCTL_MODE_GETPROPBLOB(" << param->blob_id << ") error " << (int) resp.error() << "\e[39m"
+ << frg::endlog;
+ *result = 0;
+ return EINVAL;
+ }
+
+ uint8_t *dest = reinterpret_cast<uint8_t *>(param->data);
+ for(size_t i = 0; i < resp.drm_property_blob_size(); i++) {
+ if(i >= param->length) {
+ continue;
+ }
+
+ dest[i] = resp.drm_property_blob(i);
+ }
+
+ param->length = resp.drm_property_blob_size();
+
+ *result = 0;
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETPLANE: {
+ auto param = reinterpret_cast<drm_mode_get_plane*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_plane_id(param->plane_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->crtc_id = resp.drm_crtc_id();
+ param->fb_id = resp.drm_fb_id();
+ param->possible_crtcs = resp.drm_possible_crtcs();
+ param->gamma_size = resp.drm_gamma_size();
+
+ // FIXME: this should be passed as a buffer with helix, but this has no bounded max size?
+ for(size_t i = 0; i < resp.drm_format_type_size(); i++) {
+ if(i >= param->count_format_types) {
+ break;
+ }
+ auto dest = reinterpret_cast<uint32_t *>(param->format_type_ptr);
+ dest[i] = resp.drm_format_type(i);
+ }
+
+ param->count_format_types = resp.drm_format_type_size();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETPLANERESOURCES: {
+ auto param = reinterpret_cast<drm_mode_get_plane_res *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ // FIXME: send this via a helix_ng buffer
+ for(size_t i = 0; i < resp.drm_plane_res_size(); i++) {
+ if(i >= param->count_planes) {
+ continue;
+ }
+ auto dest = reinterpret_cast<uint32_t *>(param->plane_id_ptr);
+ dest[i] = resp.drm_plane_res(i);
+ }
+
+ param->count_planes = resp.drm_plane_res_size();
+
+ *result = resp.result();
+
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETENCODER: {
+ auto param = reinterpret_cast<drm_mode_get_encoder*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_encoder_id(param->encoder_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->encoder_type = resp.drm_encoder_type();
+ param->crtc_id = resp.drm_crtc_id();
+ param->possible_crtcs = resp.drm_possible_crtcs();
+ param->possible_clones = resp.drm_possible_clones();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_CREATE_DUMB: {
+ auto param = reinterpret_cast<drm_mode_create_dumb*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_width(param->width);
+ req.set_drm_height(param->height);
+ req.set_drm_bpp(param->bpp);
+ req.set_drm_flags(param->flags);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->handle = resp.drm_handle();
+ param->pitch = resp.drm_pitch();
+ param->size = resp.drm_size();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_ADDFB: {
+ auto param = reinterpret_cast<drm_mode_fb_cmd *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_width(param->width);
+ req.set_drm_height(param->height);
+ req.set_drm_pitch(param->pitch);
+ req.set_drm_bpp(param->bpp);
+ req.set_drm_depth(param->depth);
+ req.set_drm_handle(param->handle);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->fb_id = resp.drm_fb_id();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETFB2: {
+ auto param = reinterpret_cast<drm_mode_fb_cmd2 *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(DRM_IOCTL_MODE_GETFB2);
+ req.set_drm_fb_id(param->fb_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->width = resp.drm_width();
+ param->height = resp.drm_height();
+ param->pixel_format = resp.pixel_format();
+ param->modifier[0] = resp.modifier();
+ memcpy(param->handles, resp.drm_handles().data(), sizeof(uint32_t) * resp.drm_handles_size());
+ memcpy(param->pitches, resp.drm_pitches().data(), sizeof(uint32_t) * resp.drm_pitches_size());
+ memcpy(param->offsets, resp.drm_offsets().data(), sizeof(uint32_t) * resp.drm_offsets_size());
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_ADDFB2: {
+ auto param = reinterpret_cast<drm_mode_fb_cmd2 *>(arg);
+
+ __ensure(!param->flags || param->flags == DRM_MODE_FB_MODIFIERS);
+ __ensure(!param->modifier[0] || param->modifier[0] == DRM_FORMAT_MOD_INVALID);
+ __ensure(!param->offsets[0]);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(DRM_IOCTL_MODE_ADDFB2);
+
+ req.set_drm_width(param->width);
+ req.set_drm_height(param->height);
+ req.set_drm_pitch(param->pitches[0]);
+ req.set_drm_fourcc(param->pixel_format);
+ req.set_drm_handle(param->handles[0]);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->fb_id = resp.drm_fb_id();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_RMFB: {
+ auto param = reinterpret_cast<int *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_fb_id(*param);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_MAP_DUMB: {
+ auto param = reinterpret_cast<drm_mode_map_dumb*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_handle(param->handle);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->offset = resp.drm_offset();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_GETCRTC: {
+ auto param = reinterpret_cast<drm_mode_crtc*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_crtc_id(param->crtc_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_data] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(&param->mode, sizeof(drm_mode_modeinfo)))
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_data.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->fb_id = resp.drm_fb_id();
+ param->x = resp.drm_x();
+ param->y = resp.drm_y();
+ param->gamma_size = resp.drm_gamma_size();
+ param->mode_valid = resp.drm_mode_valid();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_SETCRTC: {
+ auto param = reinterpret_cast<drm_mode_crtc*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ for(size_t i = 0; i < param->count_connectors; i++) {
+ auto dest = reinterpret_cast<uint32_t *>(param->set_connectors_ptr);
+ req.add_drm_connector_ids(dest[i]);
+ }
+ req.set_drm_x(param->x);
+ req.set_drm_y(param->y);
+ req.set_drm_crtc_id(param->crtc_id);
+ req.set_drm_fb_id(param->fb_id);
+ req.set_drm_mode_valid(param->mode_valid);
+
+ auto [offer, send_ioctl_req, send_req, send_mode, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(&param->mode, sizeof(drm_mode_modeinfo)),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(send_mode.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_OBJ_GETPROPERTIES: {
+ auto param = reinterpret_cast<drm_mode_obj_get_properties *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_count_props(param->count_props);
+ req.set_drm_obj_id(param->obj_id);
+ req.set_drm_obj_type(param->obj_type);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ auto props = reinterpret_cast<uint32_t *>(param->props_ptr);
+ auto prop_vals = reinterpret_cast<uint64_t *>(param->prop_values_ptr);
+
+ for(size_t i = 0; i < resp.drm_obj_property_ids_size(); i++) {
+ if(i >= param->count_props) {
+ break;
+ }
+ props[i] = resp.drm_obj_property_ids(i);
+ prop_vals[i] = resp.drm_obj_property_values(i);
+ }
+
+ param->count_props = resp.drm_obj_property_ids_size();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_PAGE_FLIP: {
+ auto param = reinterpret_cast<drm_mode_crtc_page_flip *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ __ensure(!(param->flags & ~DRM_MODE_PAGE_FLIP_EVENT));
+ req.set_drm_crtc_id(param->crtc_id);
+ req.set_drm_fb_id(param->fb_id);
+ req.set_drm_cookie(param->user_data);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_DIRTYFB: {
+ auto param = reinterpret_cast<drm_mode_fb_dirty_cmd*>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_fb_id(param->fb_id);
+ req.set_drm_flags(param->flags);
+ req.set_drm_color(param->color);
+ for(size_t i = 0; i < param->num_clips; i++) {
+ auto dest = reinterpret_cast<drm_clip_rect *>(param->clips_ptr);
+ managarm::fs::Rect<MemoryAllocator> clip(getSysdepsAllocator());
+ clip.set_x1(dest->x1);
+ clip.set_y1(dest->y1);
+ clip.set_x2(dest->x2);
+ clip.set_y2(dest->y2);
+ req.add_drm_clips(std::move(clip));
+ }
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }
+ }
+ case DRM_IOCTL_MODE_CURSOR: {
+ auto param = reinterpret_cast<drm_mode_cursor *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_flags(param->flags);
+ req.set_drm_crtc_id(param->crtc_id);
+
+ if (param->flags == DRM_MODE_CURSOR_MOVE) {
+ req.set_drm_x(param->x);
+ req.set_drm_y(param->y);
+ } else if (param->flags == DRM_MODE_CURSOR_BO) {
+ req.set_drm_width(param->width);
+ req.set_drm_height(param->height);
+ req.set_drm_handle(param->handle);
+ } else {
+ mlibc::infoLogger() << "\e[35mmlibc: invalid flags in DRM_IOCTL_MODE_CURSOR\e[39m" << frg::endlog;
+ return EINVAL;
+ }
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if (resp.error() == managarm::fs::Errors::NO_BACKING_DEVICE) {
+ return ENXIO;
+ }else if (resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else{
+ *result = resp.result();
+ return 0;
+ }
+ }
+ case DRM_IOCTL_MODE_DESTROY_DUMB: {
+ auto param = reinterpret_cast<drm_mode_destroy_dumb *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ req.set_drm_handle(param->handle);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_CREATEPROPBLOB: {
+ auto param = reinterpret_cast<drm_mode_create_blob *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_blob_size(param->length);
+
+ auto [offer, send_ioctl_req, send_req, blob_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(reinterpret_cast<void *>(param->data), param->length),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(blob_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->blob_id = resp.drm_blob_id();
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_DESTROYPROPBLOB: {
+ auto param = reinterpret_cast<drm_mode_destroy_blob *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_blob_id(param->blob_id);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_ATOMIC: {
+ auto param = reinterpret_cast<drm_mode_atomic *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_flags(param->flags);
+ req.set_drm_cookie(param->user_data);
+
+ size_t prop_count = 0;
+ auto objs_ptr = reinterpret_cast<uint32_t *>(param->objs_ptr);
+ auto count_props_ptr = reinterpret_cast<uint32_t *>(param->count_props_ptr);
+ auto props_ptr = reinterpret_cast<uint32_t *>(param->props_ptr);
+ auto prop_values_ptr = reinterpret_cast<uint64_t *>(param->prop_values_ptr);
+
+ for(size_t i = 0; i < param->count_objs; i++) {
+ /* list of modeobjs and their property count */
+ req.add_drm_obj_ids(objs_ptr[i]);
+ req.add_drm_prop_counts(count_props_ptr[i]);
+ prop_count += count_props_ptr[i];
+ }
+
+ for(size_t i = 0; i < prop_count; i++) {
+ /* array of property IDs */
+ req.add_drm_props(props_ptr[i]);
+ /* array of property values */
+ req.add_drm_prop_values(prop_values_ptr[i]);
+ }
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_MODE_LIST_LESSEES: {
+ mlibc::infoLogger() << "\e[35mmlibc: DRM_IOCTL_MODE_LIST_LESSEES"
+ " is not implemented correctly\e[39m" << frg::endlog;
+ return EINVAL;
+ }
+ case DRM_IOCTL_MODE_SETGAMMA: {
+ mlibc::infoLogger() << "\e[35mmlibc: DRM_IOCTL_MODE_SETGAMMA"
+ " is not implemented correctly\e[39m" << frg::endlog;
+ return 0;
+ }
+ case DRM_IOCTL_MODE_CREATE_LEASE: {
+ auto param = reinterpret_cast<drm_mode_create_lease *>(arg);
+
+ mlibc::infoLogger() << "\e[35mmlibc: DRM_IOCTL_MODE_CREATE_LEASE"
+ " is a noop\e[39m" << frg::endlog;
+ param->lessee_id = 1;
+ param->fd = fd;
+ *result = 0;
+ return 0;
+ }
+ case DRM_IOCTL_GEM_CLOSE: {
+ mlibc::infoLogger() << "\e[35mmlibc: DRM_IOCTL_GEM_CLOSE"
+ " is a noop\e[39m" << frg::endlog;
+ return 0;
+ }
+ case DRM_IOCTL_PRIME_HANDLE_TO_FD: {
+ auto param = reinterpret_cast<drm_prime_handle *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_prime_handle(param->handle);
+ req.set_drm_flags(param->flags);
+
+ auto [offer, send_ioctl_req, send_req, send_creds, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(send_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->fd = resp.drm_prime_fd();
+ *result = resp.result();
+ return 0;
+ }
+ case DRM_IOCTL_PRIME_FD_TO_HANDLE: {
+ auto param = reinterpret_cast<drm_prime_handle *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_drm_flags(param->flags);
+
+ auto [offer, send_ioctl_req, send_req, send_creds, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(getHandleForFd(param->fd)),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(send_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::FILE_NOT_FOUND) {
+ return EBADF;
+ } else {
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ }
+
+ param->handle = resp.drm_prime_handle();
+ *result = resp.result();
+ return 0;
+ }
+ }
+
+ mlibc::infoLogger() << "mlibc: Unexpected DRM ioctl with"
+ << ", number: 0x" << frg::hex_fmt(_IOC_NR(request))
+ << " (raw request: " << frg::hex_fmt(request) << ")" << frg::endlog;
+ __ensure(!"Illegal ioctl request");
+ __builtin_unreachable();
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/generic/ensure.cpp b/lib/mlibc/sysdeps/managarm/generic/ensure.cpp
new file mode 100644
index 0000000..ab0d84f
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/ensure.cpp
@@ -0,0 +1,38 @@
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/all-sysdeps.hpp>
+
+#include <hel.h>
+#include <hel-syscalls.h>
+
+void __frigg_assert_fail(const char *assertion, const char *file, unsigned int line,
+ const char *function) {
+ mlibc::panicLogger() << "In function " << function
+ << ", file " << file << ":" << line << "\n"
+ << "__ensure(" << assertion << ") failed" << frg::endlog;
+}
+
+namespace mlibc {
+ void sys_libc_log(const char *message) {
+ // This implementation is inherently signal-safe.
+ size_t n = 0;
+ while(message[n])
+ n++;
+ HEL_CHECK(helLog(message, n));
+ }
+
+ void sys_libc_panic() {
+ // This implementation is inherently signal-safe.
+ const char *message = "mlibc: Panic!";
+ size_t n = 0;
+ while(message[n])
+ n++;
+ helPanic(message, n);
+ }
+}
+
diff --git a/lib/mlibc/sysdeps/managarm/generic/entry.cpp b/lib/mlibc/sysdeps/managarm/generic/entry.cpp
new file mode 100644
index 0000000..3ff28d2
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/entry.cpp
@@ -0,0 +1,132 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/auxv.h>
+
+#include <frg/eternal.hpp>
+
+#include <bits/ensure.h>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-pipe.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/elf/startup.h>
+
+#include <protocols/posix/data.hpp>
+#include <protocols/posix/supercalls.hpp>
+
+// defined by the POSIX library
+void __mlibc_initLocale();
+
+extern "C" uintptr_t *__dlapi_entrystack();
+extern "C" void __dlapi_enter(uintptr_t *);
+
+// declared in posix-pipe.hpp
+thread_local Queue globalQueue;
+
+// TODO: clock tracker page and file table don't need to be thread-local!
+thread_local HelHandle __mlibc_posix_lane;
+thread_local void *__mlibc_clk_tracker_page;
+
+namespace {
+ thread_local unsigned __mlibc_gsf_nesting;
+ thread_local void *__mlibc_cached_thread_page;
+ thread_local HelHandle *cachedFileTable;
+
+ // This construction is a bit weird: Even though the variables above
+ // are thread_local we still protect their initialization with a pthread_once_t
+ // (instead of using a C++ constructor).
+ // We do this in order to able to clear the pthread_once_t after a fork.
+ thread_local pthread_once_t has_cached_infos = PTHREAD_ONCE_INIT;
+
+ void actuallyCacheInfos() {
+ posix::ManagarmProcessData data;
+ HEL_CHECK(helSyscall1(kHelCallSuper + posix::superGetProcessData, reinterpret_cast<HelWord>(&data)));
+
+ __mlibc_posix_lane = data.posixLane;
+ __mlibc_cached_thread_page = data.threadPage;
+ cachedFileTable = data.fileTable;
+ __mlibc_clk_tracker_page = data.clockTrackerPage;
+ }
+}
+
+SignalGuard::SignalGuard() {
+ pthread_once(&has_cached_infos, &actuallyCacheInfos);
+ if(!__mlibc_cached_thread_page)
+ return;
+ auto p = reinterpret_cast<unsigned int *>(__mlibc_cached_thread_page);
+ if(!__mlibc_gsf_nesting)
+ __atomic_store_n(p, 1, __ATOMIC_RELAXED);
+ __mlibc_gsf_nesting++;
+}
+
+SignalGuard::~SignalGuard() {
+ pthread_once(&has_cached_infos, &actuallyCacheInfos);
+ if(!__mlibc_cached_thread_page)
+ return;
+ auto p = reinterpret_cast<unsigned int *>(__mlibc_cached_thread_page);
+ __ensure(__mlibc_gsf_nesting > 0);
+ __mlibc_gsf_nesting--;
+ if(!__mlibc_gsf_nesting) {
+ unsigned int result = __atomic_exchange_n(p, 0, __ATOMIC_RELAXED);
+ if(result == 2) {
+ HEL_CHECK(helSyscall0(kHelCallSuper + posix::superSigRaise));
+ }else{
+ __ensure(result == 1);
+ }
+ }
+}
+
+MemoryAllocator &getSysdepsAllocator() {
+ // use frg::eternal to prevent a call to __cxa_atexit().
+ // this is necessary because __cxa_atexit() call this function.
+ static frg::eternal<VirtualAllocator> virtualAllocator;
+ static frg::eternal<MemoryPool> heap{virtualAllocator.get()};
+ static frg::eternal<MemoryAllocator> singleton{&heap.get()};
+ return singleton.get();
+}
+
+HelHandle getPosixLane() {
+ cacheFileTable();
+ return __mlibc_posix_lane;
+}
+
+HelHandle *cacheFileTable() {
+ // TODO: Make sure that this is signal-safe (it is called e.g. by sys_clock_get()).
+ pthread_once(&has_cached_infos, &actuallyCacheInfos);
+ return cachedFileTable;
+}
+
+HelHandle getHandleForFd(int fd) {
+ if (fd >= 512)
+ return 0;
+
+ return cacheFileTable()[fd];
+}
+
+void clearCachedInfos() {
+ has_cached_infos = PTHREAD_ONCE_INIT;
+}
+
+struct LibraryGuard {
+ LibraryGuard();
+};
+
+static LibraryGuard guard;
+
+extern char **environ;
+static mlibc::exec_stack_data __mlibc_stack_data;
+
+LibraryGuard::LibraryGuard() {
+ __mlibc_initLocale();
+
+ // Parse the exec() stack.
+ mlibc::parse_exec_stack(__dlapi_entrystack(), &__mlibc_stack_data);
+ mlibc::set_startup_data(__mlibc_stack_data.argc, __mlibc_stack_data.argv,
+ __mlibc_stack_data.envp);
+}
+
+extern "C" void __mlibc_entry(uintptr_t *entry_stack, int (*main_fn)(int argc, char *argv[], char *env[])) {
+ __dlapi_enter(entry_stack);
+ auto result = main_fn(__mlibc_stack_data.argc, __mlibc_stack_data.argv, environ);
+ exit(result);
+}
diff --git a/lib/mlibc/sysdeps/managarm/generic/file.cpp b/lib/mlibc/sysdeps/managarm/generic/file.cpp
new file mode 100644
index 0000000..1f5cba6
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/file.cpp
@@ -0,0 +1,2526 @@
+#include <asm/ioctls.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/eventfd.h>
+#include <sys/inotify.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+#include <bits/ensure.h>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/posix-pipe.hpp>
+
+#include <fs.frigg_bragi.hpp>
+#include <posix.frigg_bragi.hpp>
+
+HelHandle __mlibc_getPassthrough(int fd) {
+ auto handle = getHandleForFd(fd);
+ __ensure(handle);
+ return handle;
+}
+
+namespace mlibc {
+
+int sys_chdir(const char *path) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::CHDIR);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_fchdir(int fd) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::FCHDIR);
+ req.set_fd(fd);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_chroot(const char *path) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::CHROOT);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_mkdir(const char *path, mode_t mode) {
+ return sys_mkdirat(AT_FDCWD, path, mode);
+}
+
+int sys_mkdirat(int dirfd, const char *path, mode_t mode) {
+ (void)mode;
+ SignalGuard sguard;
+
+ managarm::posix::MkdirAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(dirfd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ALREADY_EXISTS) {
+ return EEXIST;
+ } else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::NOT_A_DIRECTORY) {
+ return ENOTDIR;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_symlink(const char *target_path, const char *link_path) {
+ return sys_symlinkat(target_path, AT_FDCWD, link_path);
+}
+
+int sys_symlinkat(const char *target_path, int dirfd, const char *link_path) {
+ SignalGuard sguard;
+
+ managarm::posix::SymlinkAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(dirfd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), link_path));
+ req.set_target_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), target_path));
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::NOT_A_DIRECTORY) {
+ return ENOTDIR;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_link(const char *old_path, const char *new_path) {
+ return sys_linkat(AT_FDCWD, old_path, AT_FDCWD, new_path, 0);
+}
+
+int sys_linkat(int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags) {
+ SignalGuard sguard;
+
+ managarm::posix::LinkAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), old_path));
+ req.set_target_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), new_path));
+ req.set_fd(olddirfd);
+ req.set_newfd(newdirfd);
+ req.set_flags(flags);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_rename(const char *path, const char *new_path) {
+ return sys_renameat(AT_FDCWD, path, AT_FDCWD, new_path);
+}
+
+int sys_renameat(int olddirfd, const char *old_path, int newdirfd, const char *new_path) {
+ SignalGuard sguard;
+
+ managarm::posix::RenameAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), old_path));
+ req.set_target_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), new_path));
+ req.set_fd(olddirfd);
+ req.set_newfd(newdirfd);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+} //namespace mlibc
+
+namespace mlibc {
+
+int sys_fcntl(int fd, int request, va_list args, int *result) {
+ SignalGuard sguard;
+ if(request == F_DUPFD) {
+ int newfd;
+ if(int e = sys_dup(fd, 0, &newfd); e)
+ return e;
+ *result = newfd;
+ return 0;
+ }else if(request == F_DUPFD_CLOEXEC) {
+ int newfd;
+ if(int e = sys_dup(fd, O_CLOEXEC, &newfd); e)
+ return e;
+ *result = newfd;
+ return 0;
+ }else if(request == F_GETFD) {
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::FD_GET_FLAGS);
+ req.set_fd(fd);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::NO_SUCH_FD)
+ return EBADF;
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *result = resp.flags();
+ return 0;
+ }else if(request == F_SETFD) {
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::FD_SET_FLAGS);
+ req.set_fd(fd);
+ req.set_flags(va_arg(args, int));
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::NO_SUCH_FD)
+ return EBADF;
+ else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS)
+ return EINVAL;
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *result = static_cast<int>(resp.error());
+ return 0;
+ }else if(request == F_GETFL) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_GET_FILE_FLAGS);
+ req.set_fd(fd);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ mlibc::infoLogger() << "\e[31mmlibc: fcntl(F_GETFL) unimplemented for this file\e[39m" << frg::endlog;
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.flags();
+ return 0;
+ }else if(request == F_SETFL) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_SET_FILE_FLAGS);
+ req.set_fd(fd);
+ req.set_flags(va_arg(args, int));
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ mlibc::infoLogger() << "\e[31mmlibc: fcntl(F_SETFL) unimplemented for this file\e[39m" << frg::endlog;
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = 0;
+ return 0;
+ }else if(request == F_SETLK) {
+ mlibc::infoLogger() << "\e[31mmlibc: F_SETLK\e[39m" << frg::endlog;
+ return 0;
+ }else if(request == F_SETLKW) {
+ mlibc::infoLogger() << "\e[31mmlibc: F_SETLKW\e[39m" << frg::endlog;
+ return 0;
+ }else if(request == F_GETLK) {
+ struct flock *lock = va_arg(args, struct flock *);
+ lock->l_type = F_UNLCK;
+ mlibc::infoLogger() << "\e[31mmlibc: F_GETLK is stubbed!\e[39m" << frg::endlog;
+ return 0;
+ }else if(request == F_ADD_SEALS) {
+ auto seals = va_arg(args, int);
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_ADD_SEALS);
+ req.set_fd(fd);
+ req.set_seals(seals);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::RecvInline()
+ ));
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ mlibc::infoLogger() << "\e[31mmlibc: fcntl(F_ADD_SEALS) unimplemented for this file\e[39m" << frg::endlog;
+ return EINVAL;
+ } else if(resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
+ return EPERM;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.seals();
+ return 0;
+ }else if(request == F_GET_SEALS) {
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_GET_SEALS);
+ req.set_fd(fd);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::RecvInline()
+ ));
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ mlibc::infoLogger() << "\e[31mmlibc: fcntl(F_GET_SEALS) unimplemented for this file\e[39m" << frg::endlog;
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.seals();
+ return 0;
+ }else{
+ mlibc::infoLogger() << "\e[31mmlibc: Unexpected fcntl() request: "
+ << request << "\e[39m" << frg::endlog;
+ return EINVAL;
+ }
+}
+
+int sys_open_dir(const char *path, int *handle) {
+ return sys_open(path, 0, 0, handle);
+}
+
+int sys_read_entries(int fd, void *buffer, size_t max_size, size_t *bytes_read) {
+ SignalGuard sguard;
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_READ_ENTRIES);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::END_OF_FILE) {
+ *bytes_read = 0;
+ return 0;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ __ensure(max_size > sizeof(struct dirent));
+ auto ent = new (buffer) struct dirent;
+ memset(ent, 0, sizeof(struct dirent));
+ memcpy(ent->d_name, resp.path().data(), resp.path().size());
+ ent->d_reclen = sizeof(struct dirent);
+ *bytes_read = sizeof(struct dirent);
+ return 0;
+ }
+}
+
+int sys_ttyname(int fd, char *buf, size_t size) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::TTY_NAME);
+ req.set_fd(fd);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::NOT_A_TTY) {
+ return ENOTTY;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ __ensure(size >= resp.path().size() + 1);
+ memcpy(buf, resp.path().data(), size);
+ buf[resp.path().size()] = '\0';
+ return 0;
+ }
+}
+
+int sys_fdatasync(int) {
+ mlibc::infoLogger() << "\e[35mmlibc: fdatasync() is a no-op\e[39m"
+ << frg::endlog;
+ return 0;
+}
+
+int sys_getcwd(char *buffer, size_t size) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::GETCWD);
+ req.set_size(size);
+
+ auto [offer, send_req, recv_resp, recv_path] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(buffer, size))
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_path.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ if(static_cast<size_t>(resp.size()) >= size)
+ return ERANGE;
+ return 0;
+}
+
+int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window) {
+ SignalGuard sguard;
+
+ managarm::posix::VmMapRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_address_hint(reinterpret_cast<uintptr_t>(hint));
+ req.set_size(size);
+ req.set_mode(prot);
+ req.set_flags(flags);
+ req.set_fd(fd);
+ req.set_rel_offset(offset);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ if(resp.error() == managarm::posix::Errors::ALREADY_EXISTS) {
+ return EEXIST;
+ }else if(resp.error() == managarm::posix::Errors::NO_MEMORY) {
+ return EFAULT;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *window = reinterpret_cast<void *>(resp.offset());
+ }
+
+ return 0;
+}
+
+int sys_vm_remap(void *pointer, size_t size, size_t new_size, void **window) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::VM_REMAP);
+ req.set_address(reinterpret_cast<uintptr_t>(pointer));
+ req.set_size(size);
+ req.set_new_size(new_size);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *window = reinterpret_cast<void *>(resp.offset());
+ return 0;
+}
+
+int sys_vm_protect(void *pointer, size_t size, int prot) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::VM_PROTECT);
+ req.set_address(reinterpret_cast<uintptr_t>(pointer));
+ req.set_size(size);
+ req.set_mode(prot);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_vm_unmap(void *pointer, size_t size) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::VM_UNMAP);
+ req.set_address(reinterpret_cast<uintptr_t>(pointer));
+ req.set_size(size);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_setsid(pid_t *sid) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::SETSID);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ACCESS_DENIED) {
+ *sid = -1;
+ return EPERM;
+ }
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *sid = resp.sid();
+ return 0;
+}
+
+int sys_tcgetattr(int fd, struct termios *attr) {
+ int result;
+ if(int e = sys_ioctl(fd, TCGETS, attr, &result); e)
+ return e;
+ return 0;
+}
+
+int sys_tcsetattr(int fd, int when, const struct termios *attr) {
+ if(when < TCSANOW || when > TCSAFLUSH)
+ return EINVAL;
+
+ if(int e = sys_ioctl(fd, TCSETS, const_cast<struct termios *>(attr), nullptr); e)
+ return e;
+ return 0;
+}
+
+int sys_tcdrain(int) {
+ mlibc::infoLogger() << "\e[35mmlibc: tcdrain() is a stub\e[39m" << frg::endlog;
+ return 0;
+}
+
+int sys_socket(int domain, int type_and_flags, int proto, int *fd) {
+ constexpr int type_mask = int(0xF);
+ constexpr int flags_mask = ~int(0xF);
+ __ensure(!((type_and_flags & flags_mask) & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)));
+
+ SignalGuard sguard;
+
+ managarm::posix::SocketRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_domain(domain);
+ req.set_socktype(type_and_flags & type_mask);
+ req.set_protocol(proto);
+ req.set_flags(type_and_flags & flags_mask);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EAFNOSUPPORT;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+ }
+}
+
+int sys_pipe(int *fds, int flags) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::PIPE_CREATE);
+ req.set_flags(flags);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ __ensure(resp.fds_size() == 2);
+ fds[0] = resp.fds(0);
+ fds[1] = resp.fds(1);
+ return 0;
+}
+
+int sys_socketpair(int domain, int type_and_flags, int proto, int *fds) {
+ constexpr int type_mask = int(0xF);
+ constexpr int flags_mask = ~int(0xF);
+ __ensure(!((type_and_flags & flags_mask) & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)));
+
+ SignalGuard sguard;
+
+ managarm::posix::SockpairRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_domain(domain);
+ req.set_socktype(type_and_flags & type_mask);
+ req.set_protocol(proto);
+ req.set_flags(type_and_flags & flags_mask);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ if(resp.error() == managarm::posix::Errors::PROTOCOL_NOT_SUPPORTED) {
+ return EPROTONOSUPPORT;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ }
+ __ensure(resp.fds_size() == 2);
+ fds[0] = resp.fds(0);
+ fds[1] = resp.fds(1);
+ return 0;
+}
+
+int sys_msg_send(int sockfd, const struct msghdr *hdr, int flags, ssize_t *length) {
+ frg::vector<HelSgItem, MemoryAllocator> sglist{getSysdepsAllocator()};
+ auto handle = getHandleForFd(sockfd);
+ if (!handle)
+ return EBADF;
+
+ size_t overall_size = 0;
+ for(int i = 0; i < hdr->msg_iovlen; i++) {
+ HelSgItem item{
+ .buffer = hdr->msg_iov[i].iov_base,
+ .length = hdr->msg_iov[i].iov_len,
+ };
+ sglist.push_back(item);
+ overall_size += hdr->msg_iov[i].iov_len;
+ }
+
+ SignalGuard sguard;
+
+ managarm::fs::SendMsgRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_flags(flags);
+ req.set_size(overall_size);
+
+ for(auto cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
+ __ensure(cmsg->cmsg_level == SOL_SOCKET);
+ if(cmsg->cmsg_type == SCM_CREDENTIALS) {
+ mlibc::infoLogger() << "mlibc: SCM_CREDENTIALS requested but we don't handle that yet!" << frg::endlog;
+ return EINVAL;
+ }
+ __ensure(cmsg->cmsg_type == SCM_RIGHTS);
+ __ensure(cmsg->cmsg_len >= sizeof(struct cmsghdr));
+
+ size_t size = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
+ __ensure(!(size % sizeof(int)));
+ for(size_t off = 0; off < size; off += sizeof(int)) {
+ int fd;
+ memcpy(&fd, CMSG_DATA(cmsg) + off, sizeof(int));
+ req.add_fds(fd);
+ }
+ }
+
+ auto [offer, send_head, send_tail, send_data, imbue_creds, send_addr, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::sendBufferSg(sglist.data(), hdr->msg_iovlen),
+ helix_ng::imbueCredentials(),
+ helix_ng::sendBuffer(hdr->msg_name, hdr->msg_namelen),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(send_data.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(send_addr.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SendMsgReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::fs::Errors::BROKEN_PIPE) {
+ return EPIPE;
+ }else if(resp.error() == managarm::fs::Errors::NOT_CONNECTED) {
+ return ENOTCONN;
+ }else if(resp.error() == managarm::fs::Errors::WOULD_BLOCK) {
+ return EAGAIN;
+ }else if(resp.error() == managarm::fs::Errors::HOST_UNREACHABLE) {
+ return EHOSTUNREACH;
+ }else if(resp.error() == managarm::fs::Errors::ACCESS_DENIED) {
+ return EACCES;
+ }else if(resp.error() == managarm::fs::Errors::NETWORK_UNREACHABLE) {
+ return ENETUNREACH;
+ }else if(resp.error() == managarm::fs::Errors::DESTINATION_ADDRESS_REQUIRED) {
+ return EDESTADDRREQ;
+ }else if(resp.error() == managarm::fs::Errors::ADDRESS_NOT_AVAILABLE) {
+ return EADDRNOTAVAIL;
+ }else if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else if(resp.error() == managarm::fs::Errors::AF_NOT_SUPPORTED) {
+ return EAFNOSUPPORT;
+ }else if(resp.error() == managarm::fs::Errors::MESSAGE_TOO_LARGE) {
+ return EMSGSIZE;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *length = resp.size();
+ return 0;
+ }
+}
+
+int sys_msg_recv(int sockfd, struct msghdr *hdr, int flags, ssize_t *length) {
+ if(!hdr->msg_iovlen) {
+ return EMSGSIZE;
+ }
+
+ auto handle = getHandleForFd(sockfd);
+ if (!handle)
+ return EBADF;
+
+ SignalGuard sguard;
+
+ managarm::fs::RecvMsgRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_flags(flags);
+ req.set_size(hdr->msg_iov[0].iov_len);
+ req.set_addr_size(hdr->msg_namelen);
+ req.set_ctrl_size(hdr->msg_controllen);
+
+ auto [offer, send_req, imbue_creds, recv_resp, recv_addr, recv_data, recv_ctrl] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(hdr->msg_name, hdr->msg_namelen),
+ helix_ng::recvBuffer(hdr->msg_iov[0].iov_base, hdr->msg_iov[0].iov_len),
+ helix_ng::recvBuffer(hdr->msg_control, hdr->msg_controllen))
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::RecvMsgReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::fs::Errors::WOULD_BLOCK) {
+ return EAGAIN;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ HEL_CHECK(recv_addr.error());
+ HEL_CHECK(recv_data.error());
+ HEL_CHECK(recv_ctrl.error());
+
+ hdr->msg_namelen = resp.addr_size();
+ hdr->msg_controllen = recv_ctrl.actualLength();
+ hdr->msg_flags = resp.flags();
+ *length = resp.ret_val();
+ return 0;
+ }
+}
+
+int sys_pselect(int, fd_set *read_set, fd_set *write_set,
+ fd_set *except_set, const struct timespec *timeout,
+ const sigset_t *sigmask, int *num_events) {
+ // TODO: Do not keep errors from epoll (?).
+ int fd = epoll_create1(0);
+ if(fd == -1)
+ return -1;
+
+ for(int k = 0; k < FD_SETSIZE; k++) {
+ struct epoll_event ev;
+ memset(&ev, 0, sizeof(struct epoll_event));
+
+ if(read_set && FD_ISSET(k, read_set))
+ ev.events |= EPOLLIN; // TODO: Additional events.
+ if(write_set && FD_ISSET(k, write_set))
+ ev.events |= EPOLLOUT; // TODO: Additional events.
+ if(except_set && FD_ISSET(k, except_set))
+ ev.events |= EPOLLPRI;
+
+ if(!ev.events)
+ continue;
+ ev.data.u32 = k;
+
+ if(epoll_ctl(fd, EPOLL_CTL_ADD, k, &ev))
+ return -1;
+ }
+
+ struct epoll_event evnts[16];
+ int n = epoll_pwait(fd, evnts, 16,
+ timeout ? (timeout->tv_sec * 1000 + timeout->tv_nsec / 100) : -1, sigmask);
+ if(n == -1)
+ return -1;
+
+ fd_set res_read_set;
+ fd_set res_write_set;
+ fd_set res_except_set;
+ FD_ZERO(&res_read_set);
+ FD_ZERO(&res_write_set);
+ FD_ZERO(&res_except_set);
+ int m = 0;
+
+ for(int i = 0; i < n; i++) {
+ int k = evnts[i].data.u32;
+
+ if(read_set && FD_ISSET(k, read_set)
+ && evnts[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP)) {
+ FD_SET(k, &res_read_set);
+ m++;
+ }
+
+ if(write_set && FD_ISSET(k, write_set)
+ && evnts[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) {
+ FD_SET(k, &res_write_set);
+ m++;
+ }
+
+ if(except_set && FD_ISSET(k, except_set)
+ && evnts[i].events & EPOLLPRI) {
+ FD_SET(k, &res_except_set);
+ m++;
+ }
+ }
+
+ if(close(fd))
+ __ensure("close() failed on epoll file");
+
+ if(read_set)
+ memcpy(read_set, &res_read_set, sizeof(fd_set));
+ if(write_set)
+ memcpy(write_set, &res_write_set, sizeof(fd_set));
+ if(except_set)
+ memcpy(except_set, &res_except_set, sizeof(fd_set));
+
+ *num_events = m;
+ return 0;
+}
+
+int sys_poll(struct pollfd *fds, nfds_t count, int timeout, int *num_events) {
+ __ensure(timeout >= 0 || timeout == -1); // TODO: Report errors correctly.
+
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::EPOLL_CALL);
+ req.set_timeout(timeout > 0 ? int64_t{timeout} * 1000000 : timeout);
+
+ for(nfds_t i = 0; i < count; i++) {
+ req.add_fds(fds[i].fd);
+ req.add_events(fds[i].events);
+ }
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ __ensure(resp.events_size() == count);
+
+ int m = 0;
+ for(nfds_t i = 0; i < count; i++) {
+ if(resp.events(i))
+ m++;
+ fds[i].revents = resp.events(i);
+ }
+
+ *num_events = m;
+ return 0;
+ }
+}
+
+int sys_epoll_create(int flags, int *fd) {
+ // Some applications assume EPOLL_CLOEXEC and O_CLOEXEC to be the same.
+ // They are on linux, but not yet on managarm.
+ __ensure(!(flags & ~(EPOLL_CLOEXEC | O_CLOEXEC)));
+
+ SignalGuard sguard;
+
+ uint32_t proto_flags = 0;
+ if(flags & EPOLL_CLOEXEC || flags & O_CLOEXEC)
+ proto_flags |= managarm::posix::OpenFlags::OF_CLOEXEC;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::EPOLL_CREATE);
+ req.set_flags(proto_flags);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+}
+
+int sys_epoll_ctl(int epfd, int mode, int fd, struct epoll_event *ev) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ if(mode == EPOLL_CTL_ADD) {
+ __ensure(ev);
+ req.set_request_type(managarm::posix::CntReqType::EPOLL_ADD);
+ req.set_flags(ev->events);
+ req.set_cookie(ev->data.u64);
+ }else if(mode == EPOLL_CTL_MOD) {
+ __ensure(ev);
+ req.set_request_type(managarm::posix::CntReqType::EPOLL_MODIFY);
+ req.set_flags(ev->events);
+ req.set_cookie(ev->data.u64);
+ }else if(mode == EPOLL_CTL_DEL) {
+ req.set_request_type(managarm::posix::CntReqType::EPOLL_DELETE);
+ }else{
+ mlibc::panicLogger() << "\e[31mmlibc: Illegal epoll_ctl() mode\e[39m" << frg::endlog;
+ }
+ req.set_fd(epfd);
+ req.set_newfd(fd);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ } else if(resp.error() == managarm::posix::Errors::ALREADY_EXISTS) {
+ return EEXIST;
+ } else if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+ return 0;
+}
+
+int sys_epoll_pwait(int epfd, struct epoll_event *ev, int n,
+ int timeout, const sigset_t *sigmask, int *raised) {
+ __ensure(timeout >= 0 || timeout == -1); // TODO: Report errors correctly.
+
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::EPOLL_WAIT);
+ req.set_fd(epfd);
+ req.set_size(n);
+ req.set_timeout(timeout > 0 ? int64_t{timeout} * 1000000 : timeout);
+ if(sigmask != NULL) {
+ req.set_sigmask((long int)*sigmask);
+ req.set_sigmask_needed(true);
+ } else {
+ req.set_sigmask_needed(false);
+ }
+
+ auto [offer, send_req, recv_resp, recv_data] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(ev, n * sizeof(struct epoll_event)))
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_data.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ __ensure(!(recv_data.actualLength() % sizeof(struct epoll_event)));
+ *raised = recv_data.actualLength() / sizeof(struct epoll_event);
+ return 0;
+}
+
+int sys_timerfd_create(int clockid, int flags, int *fd) {
+ (void) clockid;
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::TIMERFD_CREATE);
+ req.set_flags(flags);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+}
+
+int sys_timerfd_settime(int fd, int,
+ const struct itimerspec *value, struct itimerspec *oldvalue) {
+ __ensure(!oldvalue);
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::TIMERFD_SETTIME);
+ req.set_fd(fd);
+ req.set_time_secs(value->it_value.tv_sec);
+ req.set_time_nanos(value->it_value.tv_nsec);
+ req.set_interval_secs(value->it_interval.tv_sec);
+ req.set_interval_nanos(value->it_interval.tv_nsec);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_signalfd_create(const sigset_t *masks, int flags, int *fd) {
+ __ensure(!(flags & ~(SFD_CLOEXEC | SFD_NONBLOCK)));
+
+ uint32_t proto_flags = 0;
+ if(flags & SFD_CLOEXEC)
+ proto_flags |= managarm::posix::OpenFlags::OF_CLOEXEC;
+ if(flags & SFD_NONBLOCK)
+ proto_flags |= managarm::posix::OpenFlags::OF_NONBLOCK;
+
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::SIGNALFD_CREATE);
+ req.set_flags(proto_flags);
+ req.set_sigset(*masks);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+}
+
+int sys_inotify_create(int flags, int *fd) {
+ __ensure(!(flags & ~(IN_CLOEXEC | IN_NONBLOCK)));
+
+ SignalGuard sguard;
+
+ uint32_t proto_flags = 0;
+ if(flags & IN_CLOEXEC)
+ proto_flags |= managarm::posix::OpenFlags::OF_CLOEXEC;
+ if(flags & IN_NONBLOCK)
+ proto_flags |= managarm::posix::OpenFlags::OF_NONBLOCK;
+
+ managarm::posix::InotifyCreateRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_flags(proto_flags);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+}
+
+int sys_inotify_add_watch(int ifd, const char *path, uint32_t mask, int *wd) {
+ SignalGuard sguard;
+
+ managarm::posix::InotifyAddRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(ifd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+ req.set_flags(mask);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *wd = resp.wd();
+ return 0;
+ }
+}
+
+int sys_eventfd_create(unsigned int initval, int flags, int *fd) {
+ SignalGuard sguard;
+
+ uint32_t proto_flags = 0;
+ if (flags & EFD_NONBLOCK) proto_flags |= managarm::posix::OpenFlags::OF_NONBLOCK;
+ if (flags & EFD_CLOEXEC) proto_flags |= managarm::posix::OpenFlags::OF_CLOEXEC;
+ if (flags & EFD_SEMAPHORE)
+ return ENOSYS;
+
+ managarm::posix::EventfdCreateRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_flags(proto_flags);
+ req.set_initval(initval);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+}
+
+int sys_open(const char *path, int flags, mode_t mode, int *fd) {
+ return sys_openat(AT_FDCWD, path, flags, mode, fd);
+}
+
+int sys_openat(int dirfd, const char *path, int flags, mode_t mode, int *fd) {
+ SignalGuard sguard;
+
+ // We do not support O_TMPFILE.
+ if(flags & O_TMPFILE)
+ return EOPNOTSUPP;
+
+ uint32_t proto_flags = 0;
+ if(flags & O_APPEND)
+ proto_flags |= managarm::posix::OpenFlags::OF_APPEND;
+ if(flags & O_CREAT)
+ proto_flags |= managarm::posix::OpenFlags::OF_CREATE;
+ if(flags & O_EXCL)
+ proto_flags |= managarm::posix::OpenFlags::OF_EXCLUSIVE;
+ if(flags & O_NONBLOCK)
+ proto_flags |= managarm::posix::OpenFlags::OF_NONBLOCK;
+ if(flags & O_TRUNC)
+ proto_flags |= managarm::posix::OpenFlags::OF_TRUNC;
+
+ if(flags & O_CLOEXEC)
+ proto_flags |= managarm::posix::OpenFlags::OF_CLOEXEC;
+ if(flags & O_NOCTTY)
+ proto_flags |= managarm::posix::OpenFlags::OF_NOCTTY;
+
+ if(flags & O_RDONLY)
+ proto_flags |= managarm::posix::OpenFlags::OF_RDONLY;
+ else if(flags & O_WRONLY)
+ proto_flags |= managarm::posix::OpenFlags::OF_WRONLY;
+ else if(flags & O_RDWR)
+ proto_flags |= managarm::posix::OpenFlags::OF_RDWR;
+ else if(flags & O_PATH)
+ proto_flags |= managarm::posix::OpenFlags::OF_PATH;
+
+ managarm::posix::OpenAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(dirfd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+ req.set_flags(proto_flags);
+ req.set_mode(mode);
+
+ auto [offer, sendHead, sendTail, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendHead.error());
+ HEL_CHECK(sendTail.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::ALREADY_EXISTS) {
+ return EEXIST;
+ }else if(resp.error() == managarm::posix::Errors::NOT_A_DIRECTORY) {
+ return ENOTDIR;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_OPERATION_TARGET) {
+ mlibc::infoLogger() << "\e[31mmlibc: openat unimplemented for this file " << path << "\e[39m" << frg::endlog;
+ return EINVAL;
+ }else if(resp.error() == managarm::posix::Errors::NO_BACKING_DEVICE) {
+ return ENXIO;
+ }else if(resp.error() == managarm::posix::Errors::IS_DIRECTORY) {
+ return EISDIR;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+ }
+}
+
+int sys_mkfifoat(int dirfd, const char *path, mode_t mode) {
+ SignalGuard sguard;
+
+ managarm::posix::MkfifoAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(dirfd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+ req.set_mode(mode);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::ALREADY_EXISTS) {
+ return EEXIST;
+ }else if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else if(resp.error() == managarm::posix::Errors::INTERNAL_ERROR) {
+ return EIEIO;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_mknodat(int dirfd, const char *path, int mode, int dev) {
+ SignalGuard sguard;
+
+ managarm::posix::MknodAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_dirfd(dirfd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+ req.set_mode(mode);
+ req.set_device(dev);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::ALREADY_EXISTS) {
+ return EEXIST;
+ }else if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_read(int fd, void *data, size_t max_size, ssize_t *bytes_read) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::READ);
+ req.set_fd(fd);
+ req.set_size(max_size);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_req, imbue_creds, recv_resp, recv_data] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(data, max_size)
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+/* if(resp.error() == managarm::fs::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else*/
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else if(resp.error() == managarm::fs::Errors::WOULD_BLOCK) {
+ return EAGAIN;
+ }else if(resp.error() == managarm::fs::Errors::END_OF_FILE) {
+ *bytes_read = 0;
+ return 0;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ HEL_CHECK(recv_data.error());
+ *bytes_read = recv_data.actualLength();
+ return 0;
+ }
+}
+
+int sys_readv(int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_read) {
+ for(int i = 0; i < iovc; i++) {
+ ssize_t intermed = 0;
+
+ if(int e = sys_read(fd, iovs[i].iov_base, iovs[i].iov_len, &intermed); e)
+ return e;
+ else if(intermed == 0)
+ break;
+
+ *bytes_read += intermed;
+ }
+
+ return 0;
+}
+
+int sys_write(int fd, const void *data, size_t size, ssize_t *bytes_written) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::WRITE);
+ req.set_fd(fd);
+ req.set_size(size);
+
+ auto [offer, send_req, imbue_creds, send_data, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::sendBuffer(data, size),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(send_data.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ // TODO: implement NO_SUCH_FD
+/* if(resp.error() == managarm::fs::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else*/ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ return EINVAL; // FD does not support writes.
+ }else if(resp.error() == managarm::fs::Errors::NO_SPACE_LEFT) {
+ return ENOSPC;
+ }else if(resp.error() == managarm::fs::Errors::WOULD_BLOCK) {
+ return EAGAIN;
+ }else if(resp.error() == managarm::fs::Errors::NOT_CONNECTED) {
+ return ENOTCONN;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ if(bytes_written) {
+ *bytes_written = resp.size();
+ }
+ return 0;
+ }
+}
+
+int sys_pread(int fd, void *buf, size_t n, off_t off, ssize_t *bytes_read) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_PREAD);
+ req.set_fd(fd);
+ req.set_size(n);
+ req.set_offset(off);
+
+ auto [offer, send_req, imbue_creds, recv_resp, recv_data] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(buf, n))
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+/* if(resp.error() == managarm::fs::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else*/
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else if(resp.error() == managarm::fs::Errors::WOULD_BLOCK) {
+ return EAGAIN;
+ }else if(resp.error() == managarm::fs::Errors::END_OF_FILE) {
+ *bytes_read = 0;
+ return 0;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ HEL_CHECK(recv_data.error());
+ *bytes_read = recv_data.actualLength();
+ return 0;
+ }
+}
+
+int sys_pwrite(int fd, const void *buf, size_t n, off_t off, ssize_t *bytes_written) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_PWRITE);
+ req.set_fd(fd);
+ req.set_size(n);
+ req.set_offset(off);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_head, imbue_creds, to_write, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::sendBuffer(buf, n),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(to_write.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else if(resp.error() == managarm::fs::Errors::WOULD_BLOCK) {
+ return EAGAIN;
+ }else if(resp.error() == managarm::fs::Errors::NO_SPACE_LEFT) {
+ return ENOSPC;
+ }else if(resp.error() == managarm::fs::Errors::SEEK_ON_PIPE) {
+ return ESPIPE;
+ }else if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *bytes_written = n;
+ return 0;
+ }
+}
+
+int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if(!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+ req.set_rel_offset(offset);
+
+ if(whence == SEEK_SET) {
+ req.set_req_type(managarm::fs::CntReqType::SEEK_ABS);
+ }else if(whence == SEEK_CUR) {
+ req.set_req_type(managarm::fs::CntReqType::SEEK_REL);
+ }else if(whence == SEEK_END) {
+ req.set_req_type(managarm::fs::CntReqType::SEEK_EOF);
+ }else{
+ return EINVAL;
+ }
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::SEEK_ON_PIPE) {
+ return ESPIPE;
+ } else if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ } else {
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *new_offset = resp.offset();
+ return 0;
+ }
+}
+
+
+int sys_close(int fd) {
+ SignalGuard sguard;
+
+ managarm::posix::CloseRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+
+ if(resp.error() == managarm::posix::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::SUCCESS) {
+ return 0;
+ }else{
+ __ensure(!"Unexpected error");
+ __builtin_unreachable();
+ }
+}
+
+int sys_dup(int fd, int flags, int *newfd) {
+ SignalGuard sguard;
+
+ __ensure(!(flags & ~(O_CLOEXEC)));
+
+ uint32_t proto_flags = 0;
+ if(flags & O_CLOEXEC)
+ proto_flags |= managarm::posix::OpenFlags::OF_CLOEXEC;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::DUP);
+ req.set_fd(fd);
+ req.set_flags(proto_flags);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if (resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ } else if (resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ }
+
+ *newfd = resp.fd();
+ return 0;
+}
+
+int sys_dup2(int fd, int flags, int newfd) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::DUP2);
+ req.set_fd(fd);
+ req.set_newfd(newfd);
+ req.set_flags(flags);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if (resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ } else if (resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ }
+
+ return 0;
+}
+
+int sys_stat(fsfd_target fsfdt, int fd, const char *path, int flags, struct stat *result) {
+ SignalGuard sguard;
+
+ managarm::posix::FstatAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ if (fsfdt == fsfd_target::path) {
+ req.set_fd(AT_FDCWD);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+ } else if (fsfdt == fsfd_target::fd) {
+ flags |= AT_EMPTY_PATH;
+ req.set_fd(fd);
+ } else {
+ __ensure(fsfdt == fsfd_target::fd_path);
+ req.set_fd(fd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+ }
+
+ if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) {
+ return EINVAL;
+ }
+
+ if (!(flags & AT_EMPTY_PATH) && (!path || !strlen(path))) {
+ return ENOENT;
+ }
+
+ req.set_flags(flags);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::BAD_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::NOT_A_DIRECTORY) {
+ return ENOTDIR;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ memset(result, 0, sizeof(struct stat));
+
+ switch(resp.file_type()) {
+ case managarm::posix::FileType::FT_REGULAR:
+ result->st_mode = S_IFREG; break;
+ case managarm::posix::FileType::FT_DIRECTORY:
+ result->st_mode = S_IFDIR; break;
+ case managarm::posix::FileType::FT_SYMLINK:
+ result->st_mode = S_IFLNK; break;
+ case managarm::posix::FileType::FT_CHAR_DEVICE:
+ result->st_mode = S_IFCHR; break;
+ case managarm::posix::FileType::FT_BLOCK_DEVICE:
+ result->st_mode = S_IFBLK; break;
+ case managarm::posix::FileType::FT_SOCKET:
+ result->st_mode = S_IFSOCK; break;
+ case managarm::posix::FileType::FT_FIFO:
+ result->st_mode = S_IFIFO; break;
+ default:
+ __ensure(!resp.file_type());
+ }
+
+ result->st_dev = 1;
+ result->st_ino = resp.fs_inode();
+ result->st_mode |= resp.mode();
+ result->st_nlink = resp.num_links();
+ result->st_uid = resp.uid();
+ result->st_gid = resp.gid();
+ result->st_rdev = resp.ref_devnum();
+ result->st_size = resp.file_size();
+ result->st_atim.tv_sec = resp.atime_secs();
+ result->st_atim.tv_nsec = resp.atime_nanos();
+ result->st_mtim.tv_sec = resp.mtime_secs();
+ result->st_mtim.tv_nsec = resp.mtime_nanos();
+ result->st_ctim.tv_sec = resp.ctime_secs();
+ result->st_ctim.tv_nsec = resp.ctime_nanos();
+ result->st_blksize = 4096;
+ result->st_blocks = resp.file_size() / 512 + 1;
+ return 0;
+ }
+}
+
+int sys_readlink(const char *path, void *data, size_t max_size, ssize_t *length) {
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::READLINK);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+
+ auto [offer, send_req, recv_resp, recv_data] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(data, max_size))
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *length = recv_data.actualLength();
+ return 0;
+ }
+}
+
+int sys_rmdir(const char *path) {
+ SignalGuard sguard;
+
+ managarm::posix::RmdirRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_ftruncate(int fd, size_t size) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_TRUNCATE);
+ req.set_size(size);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ return EINVAL;
+ } else {
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_fallocate(int fd, off_t offset, size_t size) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_FALLOCATE);
+ req.set_rel_offset(offset);
+ req.set_size(size);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ return EINVAL;
+ }else if(resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
+ return EPERM;
+ }else if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ return 0;
+ }
+ return 0;
+}
+
+int sys_unlinkat(int fd, const char *path, int flags) {
+ SignalGuard sguard;
+
+ managarm::posix::UnlinkAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), path));
+ req.set_flags(flags);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::RESOURCE_IN_USE) {
+ return EBUSY;
+ }else if(resp.error() == managarm::posix::Errors::IS_DIRECTORY) {
+ return EISDIR;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_access(const char *path, int mode) {
+ return sys_faccessat(AT_FDCWD, path, mode, 0);
+}
+
+int sys_faccessat(int dirfd, const char *pathname, int, int flags) {
+ SignalGuard sguard;
+
+ managarm::posix::AccessAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), pathname));
+ req.set_fd(dirfd);
+ req.set_flags(flags);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_flock(int fd, int opts) {
+ SignalGuard sguard;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::FLOCK);
+ req.set_fd(fd);
+ req.set_flock_flags(opts);
+ auto handle = getHandleForFd(fd);
+ if(!handle) {
+ return EBADF;
+ }
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::WOULD_BLOCK) {
+ return EWOULDBLOCK;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_isatty(int fd) {
+ SignalGuard sguard;
+
+ managarm::posix::IsTtyRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ if(resp.error() == managarm::posix::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ if(resp.mode())
+ return 0;
+ return ENOTTY;
+ }
+}
+
+int sys_chmod(const char *pathname, mode_t mode) {
+ return sys_fchmodat(AT_FDCWD, pathname, mode, 0);
+}
+
+int sys_fchmod(int fd, mode_t mode) {
+ return sys_fchmodat(fd, "", mode, AT_EMPTY_PATH);
+}
+
+int sys_fchmodat(int fd, const char *pathname, mode_t mode, int flags) {
+ SignalGuard sguard;
+
+ managarm::posix::FchmodAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), pathname));
+ req.set_mode(mode);
+ req.set_flags(flags);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else if(resp.error() == managarm::posix::Errors::NOT_SUPPORTED) {
+ return ENOTSUP;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) {
+ (void)dirfd;
+ (void)pathname;
+ (void)owner;
+ (void)group;
+ (void)flags;
+ mlibc::infoLogger() << "mlibc: sys_fchownat is a stub!" << frg::endlog;
+ return 0;
+}
+
+int sys_umask(mode_t mode, mode_t *old) {
+ (void)mode;
+ mlibc::infoLogger() << "mlibc: sys_umask is a stub, hardcoding 022!" << frg::endlog;
+ *old = 022;
+ return 0;
+}
+
+int sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags) {
+ SignalGuard sguard;
+
+ managarm::posix::UtimensAtRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(dirfd);
+ if(pathname != nullptr)
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), pathname));
+ if(times) {
+ req.set_atimeSec(times[0].tv_sec);
+ req.set_atimeNsec(times[0].tv_nsec);
+ req.set_mtimeSec(times[1].tv_sec);
+ req.set_mtimeNsec(times[1].tv_nsec);
+ } else {
+ req.set_atimeSec(UTIME_NOW);
+ req.set_atimeNsec(UTIME_NOW);
+ req.set_mtimeSec(UTIME_NOW);
+ req.set_mtimeNsec(UTIME_NOW);
+ }
+ req.set_flags(flags);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ }else if(resp.error() == managarm::posix::Errors::NO_SUCH_FD) {
+ return EBADF;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else if(resp.error() == managarm::posix::Errors::NOT_SUPPORTED) {
+ return ENOTSUP;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_getentropy(void *buffer, size_t length) {
+ SignalGuard sguard;
+ auto p = reinterpret_cast<char *>(buffer);
+ size_t n = 0;
+
+ while(n < length) {
+ size_t chunk;
+ HEL_CHECK(helGetRandomBytes(p + n, length - n, &chunk));
+ n+= chunk;
+ }
+
+ return 0;
+}
+
+int sys_gethostname(char *buffer, size_t bufsize) {
+ SignalGuard sguard;
+ mlibc::infoLogger() << "mlibc: gethostname always returns managarm" << frg::endlog;
+ char name[10] = "managarm\0";
+ if(bufsize < 10)
+ return ENAMETOOLONG;
+ strncpy(buffer, name, 10);
+ return 0;
+}
+
+int sys_fsync(int) {
+ mlibc::infoLogger() << "mlibc: fsync is a stub" << frg::endlog;
+ return 0;
+}
+
+int sys_memfd_create(const char *name, int flags, int *fd) {
+ SignalGuard sguard;
+
+ managarm::posix::MemFdCreateRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_name(frg::string<MemoryAllocator>(getSysdepsAllocator(), name));
+ req.set_flags(flags);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }
+
+ *fd = resp.fd();
+
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_uname(struct utsname *buf) {
+ __ensure(buf);
+ mlibc::infoLogger() << "\e[31mmlibc: uname() returns static information\e[39m" << frg::endlog;
+ strcpy(buf->sysname, "Managarm");
+ strcpy(buf->nodename, "managarm");
+ strcpy(buf->release, "0.0.1-rolling");
+ strcpy(buf->version, "Managarm is not Managram");
+#if defined(__x86_64__)
+ strcpy(buf->machine, "x86_64");
+#elif defined (__aarch64__)
+ strcpy(buf->machine, "aarch64");
+#else
+# error Unknown architecture
+#endif
+
+ return 0;
+}
+
+int sys_madvise(void *, size_t, int) {
+ mlibc::infoLogger() << "mlibc: sys_madvise is a stub!" << frg::endlog;
+ return 0;
+}
+
+int sys_ptsname(int fd, char *buffer, size_t length) {
+ int index;
+ if(int e = sys_ioctl(fd, TIOCGPTN, &index, NULL); e)
+ return e;
+ if((size_t)snprintf(buffer, length, "/dev/pts/%d", index) >= length) {
+ return ERANGE;
+ }
+ return 0;
+}
+
+int sys_unlockpt(int fd) {
+ int unlock = 0;
+
+ if(int e = sys_ioctl(fd, TIOCSPTLCK, &unlock, NULL); e)
+ return e;
+
+ return 0;
+}
+
+} //namespace mlibc
+
diff --git a/lib/mlibc/sysdeps/managarm/generic/fork-exec.cpp b/lib/mlibc/sysdeps/managarm/generic/fork-exec.cpp
new file mode 100644
index 0000000..8da0e1e
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/fork-exec.cpp
@@ -0,0 +1,744 @@
+
+// for _Exit()
+#include <stdlib.h>
+
+#include <string.h>
+#include <errno.h>
+
+// for fork() and execve()
+#include <unistd.h>
+// for sched_yield()
+#include <sched.h>
+#include <stdio.h>
+// for getrusage()
+#include <sys/resource.h>
+// for waitpid()
+#include <sys/wait.h>
+#include <pthread.h>
+
+#include <bits/ensure.h>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-pipe.hpp>
+#include <mlibc/thread-entry.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <posix.frigg_bragi.hpp>
+#include <protocols/posix/supercalls.hpp>
+
+namespace mlibc {
+
+int sys_futex_tid() {
+ HelWord tid = 0;
+ HEL_CHECK(helSyscall0_1(kHelCallSuper + posix::superGetTid,
+ &tid));
+
+ return tid;
+}
+
+int sys_futex_wait(int *pointer, int expected, const struct timespec *time) {
+ // This implementation is inherently signal-safe.
+ if(time) {
+ if(helFutexWait(pointer, expected, time->tv_nsec + time->tv_sec * 1000000000))
+ return -1;
+ return 0;
+ }
+ if(helFutexWait(pointer, expected, -1))
+ return -1;
+ return 0;
+}
+
+int sys_futex_wake(int *pointer) {
+ // This implementation is inherently signal-safe.
+ if(helFutexWake(pointer))
+ return -1;
+ return 0;
+}
+
+int sys_waitpid(pid_t pid, int *status, int flags, struct rusage *ru, pid_t *ret_pid) {
+ if(ru) {
+ mlibc::infoLogger() << "mlibc: struct rusage in sys_waitpid is unsupported" << frg::endlog;
+ return ENOSYS;
+ }
+
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::WAIT);
+ req.set_pid(pid);
+ req.set_flags(flags);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ if(status)
+ *status = resp.mode();
+ *ret_pid = resp.pid();
+ return 0;
+}
+
+int sys_waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) {
+ SignalGuard sguard;
+
+ managarm::posix::WaitIdRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_idtype(idtype);
+ req.set_id(id);
+ req.set_flags(options);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::WaitIdResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ info->si_pid = resp.pid();
+ info->si_uid = resp.uid();
+ info->si_signo = SIGCHLD;
+ info->si_status = resp.sig_status();
+ info->si_code = resp.sig_code();
+ return 0;
+}
+
+void sys_exit(int status) {
+ // This implementation is inherently signal-safe.
+ HEL_CHECK(helSyscall1(kHelCallSuper + posix::superExit, status));
+ __builtin_trap();
+}
+
+void sys_yield() {
+ // This implementation is inherently signal-safe.
+ HEL_CHECK(helYield());
+}
+
+int sys_sleep(time_t *secs, long *nanos) {
+ SignalGuard sguard;
+ globalQueue.trim();
+
+ uint64_t now;
+ HEL_CHECK(helGetClock(&now));
+
+ uint64_t async_id;
+ HEL_CHECK(helSubmitAwaitClock(now + uint64_t(*secs) * 1000000000 + uint64_t(*nanos),
+ globalQueue.getQueue(), 0, &async_id));
+
+ auto element = globalQueue.dequeueSingle();
+ auto result = parseSimple(element);
+ HEL_CHECK(result->error);
+
+ *secs = 0;
+ *nanos = 0;
+
+ return 0;
+}
+
+int sys_fork(pid_t *child) {
+ // This implementation is inherently signal-safe.
+ int res;
+
+ sigset_t full_sigset;
+ res = sigfillset(&full_sigset);
+ __ensure(!res);
+
+ sigset_t former_sigset;
+ res = sigprocmask(SIG_SETMASK, &full_sigset, &former_sigset);
+ __ensure(!res);
+
+ HelWord out;
+ HEL_CHECK(helSyscall0_1(kHelCallSuper + posix::superFork, &out));
+ *child = out;
+
+ if(!out) {
+ clearCachedInfos();
+ globalQueue.recreateQueue();
+ }
+
+ res = sigprocmask(SIG_SETMASK, &former_sigset, nullptr);
+ __ensure(!res);
+
+ return 0;
+}
+
+int sys_execve(const char *path, char *const argv[], char *const envp[]) {
+ // TODO: Make this function signal-safe!
+ frg::string<MemoryAllocator> args_area(getSysdepsAllocator());
+ for(auto it = argv; *it; ++it)
+ args_area += frg::string_view{*it, strlen(*it) + 1};
+
+ frg::string<MemoryAllocator> env_area(getSysdepsAllocator());
+ for(auto it = envp; *it; ++it)
+ env_area += frg::string_view{*it, strlen(*it) + 1};
+
+ uintptr_t out;
+
+ HEL_CHECK(helSyscall6_1(kHelCallSuper + posix::superExecve,
+ reinterpret_cast<uintptr_t>(path),
+ strlen(path),
+ reinterpret_cast<uintptr_t>(args_area.data()),
+ args_area.size(),
+ reinterpret_cast<uintptr_t>(env_area.data()),
+ env_area.size(),
+ &out));
+
+ return out;
+}
+
+gid_t sys_getgid() {
+ SignalGuard sguard;
+
+ managarm::posix::GetGidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return resp.uid();
+}
+
+int sys_setgid(gid_t gid) {
+ SignalGuard sguard;
+
+ managarm::posix::SetGidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_uid(gid);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ACCESS_DENIED) {
+ return EPERM;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+gid_t sys_getegid() {
+ SignalGuard sguard;
+
+ managarm::posix::GetEgidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return resp.uid();
+}
+
+int sys_setegid(gid_t egid) {
+ SignalGuard sguard;
+
+ managarm::posix::SetEgidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_uid(egid);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ACCESS_DENIED) {
+ return EPERM;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+uid_t sys_getuid() {
+ SignalGuard sguard;
+
+ managarm::posix::GetUidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return resp.uid();
+}
+
+int sys_setuid(uid_t uid) {
+ SignalGuard sguard;
+
+ managarm::posix::SetUidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_uid(uid);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ACCESS_DENIED) {
+ return EPERM;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+uid_t sys_geteuid() {
+ SignalGuard sguard;
+
+ managarm::posix::GetEuidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return resp.uid();
+}
+
+int sys_seteuid(uid_t euid) {
+ SignalGuard sguard;
+
+ managarm::posix::SetEuidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_uid(euid);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::ACCESS_DENIED) {
+ return EPERM;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+pid_t sys_gettid() {
+ // TODO: use an actual gettid syscall.
+ return sys_getpid();
+}
+
+pid_t sys_getpid() {
+ SignalGuard sguard;
+
+ managarm::posix::GetPidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return resp.pid();
+}
+
+pid_t sys_getppid() {
+ SignalGuard sguard;
+
+ managarm::posix::GetPpidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return resp.pid();
+}
+
+int sys_getsid(pid_t pid, pid_t *sid) {
+ SignalGuard sguard;
+
+ managarm::posix::GetSidRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_pid(pid);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::NO_SUCH_RESOURCE) {
+ *sid = 0;
+ return ESRCH;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *sid = resp.pid();
+ return 0;
+ }
+}
+
+int sys_getpgid(pid_t pid, pid_t *pgid) {
+ SignalGuard sguard;
+
+ managarm::posix::GetPgidRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_pid(pid);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::NO_SUCH_RESOURCE) {
+ *pgid = 0;
+ return ESRCH;
+ } else {
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *pgid = resp.pid();
+ return 0;
+ }
+}
+
+int sys_setpgid(pid_t pid, pid_t pgid) {
+ SignalGuard sguard;
+
+ managarm::posix::SetPgidRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_pid(pid);
+ req.set_pgid(pgid);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::posix::Errors::INSUFFICIENT_PERMISSION) {
+ return EPERM;
+ }else if(resp.error() == managarm::posix::Errors::NO_SUCH_RESOURCE) {
+ return ESRCH;
+ }else if(resp.error() == managarm::posix::Errors::ACCESS_DENIED) {
+ return EACCES;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+}
+
+int sys_getrusage(int scope, struct rusage *usage) {
+ memset(usage, 0, sizeof(struct rusage));
+
+ SignalGuard sguard;
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::GET_RESOURCE_USAGE);
+ req.set_mode(scope);
+
+ auto [offer, send_head, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+
+ usage->ru_utime.tv_sec = resp.ru_user_time() / 1'000'000'000;
+ usage->ru_utime.tv_usec = (resp.ru_user_time() % 1'000'000'000) / 1'000;
+
+ return 0;
+}
+
+int sys_getschedparam(void *tcb, int *policy, struct sched_param *param) {
+ if(tcb != mlibc::get_current_tcb()) {
+ return ESRCH;
+ }
+
+ *policy = SCHED_OTHER;
+ int prio = 0;
+ // TODO(no92): use helGetPriority(kHelThisThread) here
+ mlibc::infoLogger() << "\e[31mlibc: sys_getschedparam always returns priority 0\e[39m" << frg::endlog;
+ param->sched_priority = prio;
+
+ return 0;
+}
+
+int sys_setschedparam(void *tcb, int policy, const struct sched_param *param) {
+ if(tcb != mlibc::get_current_tcb()) {
+ return ESRCH;
+ }
+
+ if(policy != SCHED_OTHER) {
+ return EINVAL;
+ }
+
+ HEL_CHECK(helSetPriority(kHelThisThread, param->sched_priority));
+
+ return 0;
+}
+
+int sys_clone(void *tcb, pid_t *pid_out, void *stack) {
+ HelWord pid = 0;
+ HEL_CHECK(helSyscall2_1(kHelCallSuper + posix::superClone,
+ reinterpret_cast<HelWord>(__mlibc_start_thread),
+ reinterpret_cast<HelWord>(stack),
+ &pid));
+
+ if (pid_out)
+ *pid_out = pid;
+
+ return 0;
+}
+
+int sys_tcb_set(void *pointer) {
+#if defined(__aarch64__)
+ uintptr_t addr = reinterpret_cast<uintptr_t>(pointer);
+ addr += sizeof(Tcb) - 0x10;
+ asm volatile ("msr tpidr_el0, %0" :: "r"(addr));
+#else
+ HEL_CHECK(helWriteFsBase(pointer));
+#endif
+ return 0;
+}
+
+void sys_thread_exit() {
+ // This implementation is inherently signal-safe.
+ HEL_CHECK(helSyscall1(kHelCallSuper + posix::superExit, 0));
+ __builtin_trap();
+}
+
+int sys_thread_setname(void *tcb, const char *name) {
+ if(strlen(name) > 15) {
+ return ERANGE;
+ }
+
+ auto t = reinterpret_cast<Tcb *>(tcb);
+ char *path;
+ int cs = 0;
+
+ if(asprintf(&path, "/proc/self/task/%d/comm", t->tid) < 0) {
+ return ENOMEM;
+ }
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+ int fd;
+ if(int e = sys_open(path, O_WRONLY, 0, &fd); e) {
+ return e;
+ }
+
+ if(int e = sys_write(fd, name, strlen(name) + 1, NULL)) {
+ return e;
+ }
+
+ sys_close(fd);
+
+ pthread_setcancelstate(cs, 0);
+
+ return 0;
+}
+
+int sys_thread_getname(void *tcb, char *name, size_t size) {
+ auto t = reinterpret_cast<Tcb *>(tcb);
+ char *path;
+ int cs = 0;
+ ssize_t real_size = 0;
+
+ if(asprintf(&path, "/proc/self/task/%d/comm", t->tid) < 0) {
+ return ENOMEM;
+ }
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+ int fd;
+ if(int e = sys_open(path, O_RDONLY | O_CLOEXEC, 0, &fd); e) {
+ return e;
+ }
+
+ if(int e = sys_read(fd, name, size, &real_size)) {
+ return e;
+ }
+
+ name[real_size - 1] = 0;
+ sys_close(fd);
+
+ pthread_setcancelstate(cs, 0);
+
+ if(static_cast<ssize_t>(size) <= real_size) {
+ return ERANGE;
+ }
+
+ return 0;
+}
+
+
+} //namespace mlibc
+
diff --git a/lib/mlibc/sysdeps/managarm/generic/ioctl.cpp b/lib/mlibc/sysdeps/managarm/generic/ioctl.cpp
new file mode 100644
index 0000000..384c09e
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/ioctl.cpp
@@ -0,0 +1,708 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/cdrom.h>
+#include <linux/input.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <sys/ioctl.h>
+
+#include <bits/ensure.h>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-pipe.hpp>
+
+#include <fs.frigg_bragi.hpp>
+#include <posix.frigg_bragi.hpp>
+
+namespace mlibc {
+
+static constexpr bool logIoctls = false;
+
+int ioctl_drm(int fd, unsigned long request, void *arg, int *result, HelHandle handle);
+
+int sys_ioctl(int fd, unsigned long request, void *arg, int *result) {
+ if(logIoctls)
+ mlibc::infoLogger() << "mlibc: ioctl with"
+ << " type: 0x" << frg::hex_fmt(_IOC_TYPE(request))
+ << ", number: 0x" << frg::hex_fmt(_IOC_NR(request))
+ << " (raw request: " << frg::hex_fmt(request) << ")"
+ << " on fd " << fd << frg::endlog;
+
+ SignalGuard sguard;
+ auto handle = getHandleForFd(fd);
+ if(!handle)
+ return EBADF;
+
+ if(_IOC_TYPE(request) == 'd') {
+ return ioctl_drm(fd, request, arg, result, handle);
+ }
+
+ managarm::fs::IoctlRequest<MemoryAllocator> ioctl_req(getSysdepsAllocator());
+
+ switch(request) {
+ case FIONBIO: {
+ auto mode = reinterpret_cast<int *>(arg);
+ int flags = fcntl(fd, F_GETFL, 0);
+ if(*mode) {
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ }else{
+ fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ return 0;
+ }
+ case FIONREAD: {
+ auto argp = reinterpret_cast<int *>(arg);
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ if(!argp)
+ return EINVAL;
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(FIONREAD);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_CONNECTED) {
+ return ENOTCONN;
+ } else {
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *argp = resp.fionread_count();
+
+ return 0;
+ }
+ }
+ case FIOCLEX: {
+ managarm::posix::IoctlFioclexRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ if(recvResp.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+ case TCGETS: {
+ auto param = reinterpret_cast<struct termios *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_attrs] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(param, sizeof(struct termios))
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_attrs.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ __ensure(recv_attrs.actualLength() == sizeof(struct termios));
+ *result = resp.result();
+ return 0;
+ }
+ case TCSETS: {
+ auto param = reinterpret_cast<struct termios *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, send_attrs, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(param, sizeof(struct termios)),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(send_attrs.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ if(result)
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCSCTTY: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else if(resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
+ return EPERM;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGWINSZ: {
+ auto param = reinterpret_cast<struct winsize *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET)
+ return EINVAL;
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ param->ws_col = resp.pts_width();
+ param->ws_row = resp.pts_height();
+ param->ws_xpixel = resp.pts_pixel_width();
+ param->ws_ypixel = resp.pts_pixel_height();
+ return 0;
+ }
+ case TIOCSWINSZ: {
+ auto param = reinterpret_cast<const struct winsize *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_pts_width(param->ws_col);
+ req.set_pts_height(param->ws_row);
+ req.set_pts_pixel_width(param->ws_xpixel);
+ req.set_pts_pixel_height(param->ws_ypixel);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGPTN: {
+ auto param = reinterpret_cast<int *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *param = resp.pts_index();
+ if(result)
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGPGRP: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error())
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
+ return ENOTTY;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ *static_cast<int *>(arg) = resp.pid();
+ return 0;
+ }
+ case TIOCSPGRP: {
+ auto param = reinterpret_cast<int *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_pgid(*param);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
+ return EPERM;
+ } else if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGSID: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ if(send_ioctl_req.error())
+ return EINVAL;
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error())
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ if(imbue_creds.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
+ return ENOTTY;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ *static_cast<int *>(arg) = resp.pid();
+ return 0;
+ }
+ case CDROM_GET_CAPABILITY: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ if(send_ioctl_req.error())
+ return EINVAL;
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error())
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
+ return ENOTTY;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }
+ } // end of switch()
+
+
+ if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGVERSION)) {
+ *reinterpret_cast<int *>(arg) = 0x010001; // should be EV_VERSION
+ *result = 0;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGID)) {
+ memset(arg, 0, sizeof(struct input_id));
+ *result = 0;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGNAME(0))) {
+ const char *s = "Managarm generic evdev";
+ auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
+ memcpy(arg, s, chunk);
+ *result = chunk;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGPHYS(0))) {
+ // Returns the sysfs path of the device.
+ const char *s = "input0";
+ auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
+ memcpy(arg, s, chunk);
+ *result = chunk;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGUNIQ(0))) {
+ // Returns a unique ID for the device.
+ const char *s = "0";
+ auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
+ memcpy(arg, s, chunk);
+ *result = chunk;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGPROP(0))) {
+ // Returns a bitmask of properties of the device.
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGKEY(0))) {
+ // Returns the current key state.
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGLED(0))) {
+ // Returns the current LED state.
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGSW(0))) {
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) >= _IOC_NR(EVIOCGBIT(0, 0))
+ && _IOC_NR(request) <= _IOC_NR(EVIOCGBIT(EV_MAX, 0))) {
+ // Returns a bitmask of capabilities of the device.
+ // If type is zero, return a mask of supported types.
+ // As EV_SYN is zero, this implies that it is impossible
+ // to get the mask of supported synthetic events.
+ auto type = _IOC_NR(request) - _IOC_NR(EVIOCGBIT(0, 0));
+ if(!type) {
+ // TODO: Check with the Linux ABI if we have to do this.
+ memset(arg, 0, _IOC_SIZE(request));
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(EVIOCGBIT(0, 0));
+ req.set_size(_IOC_SIZE(request));
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_data] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(arg, _IOC_SIZE(request)))
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_data.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = recv_data.actualLength();
+ return 0;
+ }else{
+ // TODO: Check with the Linux ABI if we have to do this.
+ memset(arg, 0, _IOC_SIZE(request));
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(EVIOCGBIT(1, 0));
+ req.set_input_type(type);
+ req.set_size(_IOC_SIZE(request));
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_data] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(arg, _IOC_SIZE(request)))
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_data.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = recv_data.actualLength();
+ return 0;
+ }
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCSCLOCKID)) {
+ auto param = reinterpret_cast<int *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_input_clock(*param);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) >= _IOC_NR(EVIOCGABS(0))
+ && _IOC_NR(request) <= _IOC_NR(EVIOCGABS(ABS_MAX))) {
+ auto param = reinterpret_cast<struct input_absinfo *>(arg);
+
+ auto type = _IOC_NR(request) - _IOC_NR(EVIOCGABS(0));
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(EVIOCGABS(0));
+ req.set_input_type(type);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->value = resp.input_value();
+ param->minimum = resp.input_min();
+ param->maximum = resp.input_max();
+ param->fuzz = resp.input_fuzz();
+ param->flat = resp.input_flat();
+ param->resolution = resp.input_resolution();
+
+ *result = resp.result();
+ return 0;
+ }else if(request == KDSETMODE) {
+ auto param = reinterpret_cast<unsigned int *>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: KD_SETMODE(" << frg::hex_fmt(param) << ") is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == KDGETMODE) {
+ auto param = reinterpret_cast<unsigned int *>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: KD_GETMODE is a no-op" << frg::endlog;
+ *param = 0;
+
+ *result = 0;
+ return 0;
+ }else if(request == KDSKBMODE) {
+ auto param = reinterpret_cast<long>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: KD_SKBMODE(" << frg::hex_fmt(param) << ") is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == VT_SETMODE) {
+ // auto param = reinterpret_cast<struct vt_mode *>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: VT_SETMODE is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == VT_GETSTATE) {
+ auto param = reinterpret_cast<struct vt_stat *>(arg);
+
+ param->v_active = 0;
+ param->v_signal = 0;
+ param->v_state = 0;
+
+ mlibc::infoLogger() << "\e[35mmlibc: VT_GETSTATE is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == VT_ACTIVATE || request == VT_WAITACTIVE) {
+ mlibc::infoLogger() << "\e[35mmlibc: VT_ACTIVATE/VT_WAITACTIVE are no-ops" << frg::endlog;
+ *result = 0;
+ return 0;
+ }else if(request == TIOCSPTLCK) {
+ mlibc::infoLogger() << "\e[35mmlibc: TIOCSPTLCK is a no-op" << frg::endlog;
+ if(result)
+ *result = 0;
+ return 0;
+ }
+
+ mlibc::infoLogger() << "mlibc: Unexpected ioctl with"
+ << " type: 0x" << frg::hex_fmt(_IOC_TYPE(request))
+ << ", number: 0x" << frg::hex_fmt(_IOC_NR(request))
+ << " (raw request: " << frg::hex_fmt(request) << ")" << frg::endlog;
+ __ensure(!"Illegal ioctl request");
+ __builtin_unreachable();
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/generic/memory.cpp b/lib/mlibc/sysdeps/managarm/generic/memory.cpp
new file mode 100644
index 0000000..91e47af
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/memory.cpp
@@ -0,0 +1,30 @@
+
+#include <string.h>
+
+#include <bits/ensure.h>
+#include <mlibc/allocator.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <protocols/posix/supercalls.hpp>
+
+#include <hel.h>
+#include <hel-syscalls.h>
+
+namespace mlibc {
+
+int sys_anon_allocate(size_t size, void **pointer) {
+ // This implementation is inherently signal-safe.
+ __ensure(!(size & 0xFFF));
+ HelWord out;
+ HEL_CHECK(helSyscall1_1(kHelCallSuper + posix::superAnonAllocate, size, &out));
+ *pointer = reinterpret_cast<void *>(out);
+ return 0;
+}
+
+int sys_anon_free(void *pointer, size_t size) {
+ // This implementation is inherently signal-safe.
+ HEL_CHECK(helSyscall2(kHelCallSuper + posix::superAnonDeallocate, (HelWord)pointer, size));
+ return 0;
+}
+
+} //namespace mlibc
+
diff --git a/lib/mlibc/sysdeps/managarm/generic/mount.cpp b/lib/mlibc/sysdeps/managarm/generic/mount.cpp
new file mode 100644
index 0000000..5677b20
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/mount.cpp
@@ -0,0 +1,44 @@
+#include <errno.h>
+#include <string.h>
+#include <sys/mount.h>
+
+#include <bits/ensure.h>
+#include <mlibc/allocator.hpp>
+#include <mlibc/posix-pipe.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <posix.frigg_bragi.hpp>
+#include <bragi/helpers-frigg.hpp>
+
+namespace mlibc {
+
+int sys_mount(const char *source, const char *target,
+ const char *fstype, unsigned long, const void *) {
+ SignalGuard sguard;
+
+ managarm::posix::MountRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), source));
+ req.set_target_path(frg::string<MemoryAllocator>(getSysdepsAllocator(), target));
+ req.set_fs_type(frg::string<MemoryAllocator>(getSysdepsAllocator(), fstype));
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ auto resp = *bragi::parse_head_only<managarm::posix::SvrResponse>(recv_resp, getSysdepsAllocator());
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND)
+ return ENOENT;
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/generic/net.cpp b/lib/mlibc/sysdeps/managarm/generic/net.cpp
new file mode 100644
index 0000000..63f2d0c
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/net.cpp
@@ -0,0 +1,57 @@
+#include <mlibc/all-sysdeps.hpp>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+namespace mlibc {
+
+int sys_if_indextoname(unsigned int index, char *name) {
+ int fd = 0;
+ int r = sys_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, AF_UNSPEC, &fd);
+
+ if(r)
+ return r;
+
+ struct ifreq ifr;
+ ifr.ifr_ifindex = index;
+
+ int res = 0;
+ int ret = sys_ioctl(fd, SIOCGIFNAME, &ifr, &res);
+ close(fd);
+
+ if(ret) {
+ if(ret == ENODEV)
+ return ENXIO;
+ return ret;
+ }
+
+ strncpy(name, ifr.ifr_name, IF_NAMESIZE);
+
+ return 0;
+}
+
+int sys_if_nametoindex(const char *name, unsigned int *ret) {
+ int fd = 0;
+ int r = sys_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, AF_UNSPEC, &fd);
+
+ if(r)
+ return r;
+
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+
+ int res = 0;
+ r = sys_ioctl(fd, SIOCGIFINDEX, &ifr, &res);
+ close(fd);
+
+ if(r)
+ return r;
+
+ *ret = ifr.ifr_ifindex;
+
+ return 0;
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/generic/sched.cpp b/lib/mlibc/sysdeps/managarm/generic/sched.cpp
new file mode 100644
index 0000000..bce8db8
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/sched.cpp
@@ -0,0 +1,102 @@
+#include <bits/ensure.h>
+#include <unistd.h>
+
+#include <hel.h>
+#include <hel-syscalls.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/posix-pipe.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+#include <posix.frigg_bragi.hpp>
+
+namespace mlibc {
+
+int sys_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask) {
+ return sys_getthreadaffinity(pid, cpusetsize, mask);
+}
+
+int sys_getthreadaffinity(pid_t tid, size_t cpusetsize, cpu_set_t *mask) {
+ SignalGuard sguard;
+
+ managarm::posix::GetAffinityRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_pid(tid);
+ req.set_size(cpusetsize);
+
+ auto [offer, send_head, recv_resp, recv_data] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(mask, cpusetsize)
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ } else if(resp.error() != managarm::posix::Errors::SUCCESS) {
+ mlibc::infoLogger() << "mlibc: got unexpected error from posix in sys_getaffinity!" << frg::endlog;
+ return EIEIO;
+ }
+ HEL_CHECK(recv_data.error());
+
+ return 0;
+}
+
+int sys_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask) {
+ return sys_setthreadaffinity(pid, cpusetsize, mask);
+}
+
+int sys_setthreadaffinity(pid_t tid, size_t cpusetsize, const cpu_set_t *mask) {
+ SignalGuard sguard;
+
+ frg::vector<uint8_t, MemoryAllocator> affinity_mask(getSysdepsAllocator());
+ affinity_mask.resize(cpusetsize);
+ memcpy(affinity_mask.data(), mask, cpusetsize);
+ managarm::posix::SetAffinityRequest<MemoryAllocator> req(getSysdepsAllocator());
+
+ req.set_pid(tid);
+ req.set_mask(affinity_mask);
+
+ auto [offer, send_head, send_tail, recv_resp] =
+ exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_head.error());
+ HEL_CHECK(send_tail.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ } else if(resp.error() != managarm::posix::Errors::SUCCESS) {
+ mlibc::infoLogger() << "mlibc: got unexpected error from posix in sys_getaffinity!" << frg::endlog;
+ return EIEIO;
+ }
+
+ return 0;
+}
+
+int sys_getcpu(int *cpu) {
+ HEL_CHECK(helGetCurrentCpu(cpu));
+ return 0;
+}
+
+}
diff --git a/lib/mlibc/sysdeps/managarm/generic/signals.cpp b/lib/mlibc/sysdeps/managarm/generic/signals.cpp
new file mode 100644
index 0000000..486f3d4
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/signals.cpp
@@ -0,0 +1,139 @@
+#include <bits/ensure.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <hel.h>
+#include <hel-syscalls.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/posix-pipe.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <posix.frigg_bragi.hpp>
+
+#include <bragi/helpers-frigg.hpp>
+#include <helix/ipc-structs.hpp>
+
+#include <protocols/posix/supercalls.hpp>
+
+extern "C" void __mlibc_signal_restore();
+
+namespace mlibc {
+
+int sys_sigprocmask(int how, const sigset_t *set, sigset_t *retrieve) {
+ // This implementation is inherently signal-safe.
+ uint64_t former, unused;
+ if(set) {
+ HEL_CHECK(helSyscall2_2(kHelObserveSuperCall + posix::superSigMask, how, *set, &former, &unused));
+ }else{
+ HEL_CHECK(helSyscall2_2(kHelObserveSuperCall + posix::superSigMask, 0, 0, &former, &unused));
+ }
+ if(retrieve)
+ *retrieve = former;
+ return 0;
+}
+
+int sys_sigaction(int number, const struct sigaction *__restrict action,
+ struct sigaction *__restrict saved_action) {
+ SignalGuard sguard;
+
+ // TODO: Respect restorer. __ensure(!(action->sa_flags & SA_RESTORER));
+
+ managarm::posix::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_request_type(managarm::posix::CntReqType::SIG_ACTION);
+ req.set_sig_number(number);
+ if(action) {
+ req.set_mode(1);
+ req.set_flags(action->sa_flags);
+ req.set_sig_mask(action->sa_mask);
+ if(action->sa_flags & SA_SIGINFO) {
+ req.set_sig_handler(reinterpret_cast<uintptr_t>(action->sa_sigaction));
+ }else{
+ req.set_sig_handler(reinterpret_cast<uintptr_t>(action->sa_handler));
+ }
+ req.set_sig_restorer(reinterpret_cast<uintptr_t>(&__mlibc_signal_restore));
+ } else {
+ req.set_mode(0);
+ }
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::posix::Errors::ILLEGAL_REQUEST) {
+ // This is only returned for servers, not for normal userspace.
+ return ENOSYS;
+ }else if(resp.error() == managarm::posix::Errors::ILLEGAL_ARGUMENTS) {
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+
+ if(saved_action) {
+ saved_action->sa_flags = resp.flags();
+ saved_action->sa_mask = resp.sig_mask();
+ if(resp.flags() & SA_SIGINFO) {
+ saved_action->sa_sigaction =
+ reinterpret_cast<void (*)(int, siginfo_t *, void *)>(resp.sig_handler());
+ }else{
+ saved_action->sa_handler = reinterpret_cast<void (*)(int)>(resp.sig_handler());
+ }
+ // TODO: saved_action->sa_restorer = resp.sig_restorer;
+ }
+ return 0;
+}
+
+int sys_kill(int pid, int number) {
+ // This implementation is inherently signal-safe.
+ HEL_CHECK(helSyscall2(kHelObserveSuperCall + posix::superSigKill, pid, number));
+ return 0;
+}
+
+int sys_tgkill(int, int tid, int number) {
+ return sys_kill(tid, number);
+}
+
+int sys_sigaltstack(const stack_t *ss, stack_t *oss) {
+ HelWord out;
+
+ // This implementation is inherently signal-safe.
+ HEL_CHECK(helSyscall2_1(kHelObserveSuperCall + posix::superSigAltStack,
+ reinterpret_cast<HelWord>(ss),
+ reinterpret_cast<HelWord>(oss),
+ &out));
+
+ return out;
+}
+
+int sys_sigsuspend(const sigset_t *set) {
+ //SignalGuard sguard;
+ uint64_t former, seq, unused;
+
+ HEL_CHECK(helSyscall2_2(kHelObserveSuperCall + posix::superSigMask, SIG_SETMASK, *set, &former, &seq));
+ HEL_CHECK(helSyscall1(kHelObserveSuperCall + posix::superSigSuspend, seq));
+ HEL_CHECK(helSyscall2_2(kHelObserveSuperCall + posix::superSigMask, SIG_SETMASK, former, &unused, &unused));
+
+ return EINTR;
+}
+
+int sys_sigpending(sigset_t *set) {
+ uint64_t pendingMask;
+
+ HEL_CHECK(helSyscall0_1(kHelObserveSuperCall + posix::superSigGetPending, &pendingMask));
+ *set = pendingMask;
+
+ return 0;
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/generic/socket.cpp b/lib/mlibc/sysdeps/managarm/generic/socket.cpp
new file mode 100644
index 0000000..5bdc1fc
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/socket.cpp
@@ -0,0 +1,423 @@
+
+#include <bits/ensure.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-pipe.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <fs.frigg_bragi.hpp>
+#include <posix.frigg_bragi.hpp>
+
+namespace {
+
+int fcntl_helper(int fd, int request, int *result, ...) {
+ va_list args;
+ va_start(args, result);
+ if(!mlibc::sys_fcntl) {
+ return ENOSYS;
+ }
+ int ret = mlibc::sys_fcntl(fd, request, args, result);
+ va_end(args);
+ return ret;
+}
+
+}
+
+namespace mlibc {
+
+int sys_accept(int fd, int *newfd, struct sockaddr *addr_ptr, socklen_t *addr_length, int flags) {
+ SignalGuard sguard;
+
+ managarm::posix::AcceptRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ if(resp.error() == managarm::posix::Errors::WOULD_BLOCK) {
+ return EWOULDBLOCK;
+ }else{
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *newfd = resp.fd();
+ }
+
+ if(addr_ptr && addr_length) {
+ if(int e = mlibc::sys_peername(*newfd, addr_ptr, *addr_length, addr_length); e) {
+ errno = e;
+ return -1;
+ }
+ }
+
+ if(flags & SOCK_NONBLOCK) {
+ int fcntl_ret = 0;
+ fcntl_helper(*newfd, F_GETFL, &fcntl_ret);
+ fcntl_helper(*newfd, F_SETFL, &fcntl_ret, fcntl_ret | O_NONBLOCK);
+ }
+
+ if(flags & SOCK_CLOEXEC) {
+ int fcntl_ret = 0;
+ fcntl_helper(*newfd, F_GETFD, &fcntl_ret);
+ fcntl_helper(*newfd, F_SETFD, &fcntl_ret, fcntl_ret | FD_CLOEXEC);
+ }
+
+ return 0;
+}
+
+int sys_bind(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length) {
+ SignalGuard sguard;
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_BIND);
+
+ auto [offer, send_req, send_creds, send_buf, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::sendBuffer(addr_ptr, addr_length),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(send_creds.error());
+ HEL_CHECK(send_buf.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ } else if(resp.error() == managarm::fs::Errors::ADDRESS_IN_USE) {
+ return EADDRINUSE;
+ } else if(resp.error() == managarm::fs::Errors::ALREADY_EXISTS) {
+ return EINVAL;
+ } else if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ return EINVAL;
+ } else if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ } else if(resp.error() == managarm::fs::Errors::ACCESS_DENIED) {
+ return EACCES;
+ } else if(resp.error() == managarm::fs::Errors::ADDRESS_NOT_AVAILABLE) {
+ return EADDRNOTAVAIL;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_connect(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_CONNECT);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_req, imbue_creds, send_addr, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::sendBuffer(const_cast<struct sockaddr *>(addr_ptr), addr_length),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(send_addr.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::FILE_NOT_FOUND) {
+ return ENOENT;
+ } else if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }
+
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_sockname(int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length,
+ socklen_t *actual_length) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_SOCKNAME);
+ req.set_fd(fd);
+ req.set_size(max_addr_length);
+
+ auto [offer, send_req, recv_resp, recv_addr] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(addr_ptr, max_addr_length))
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ return ENOTSOCK;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ HEL_CHECK(recv_addr.error());
+ *actual_length = resp.file_size();
+ return 0;
+}
+
+int sys_peername(int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length,
+ socklen_t *actual_length) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_PEERNAME);
+ req.set_fd(fd);
+ req.set_size(max_addr_length);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, sendReq, recvResp, recvData] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(addr_ptr, max_addr_length)
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ if(recvResp.error() == kHelErrDismissed)
+ return ENOTSOCK;
+ HEL_CHECK(recvResp.error());
+ HEL_CHECK(recvData.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET) {
+ return ENOTSOCK;
+ }else if(resp.error() == managarm::fs::Errors::NOT_CONNECTED) {
+ return ENOTCONN;
+ }else{
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *actual_length = resp.file_size();
+ return 0;
+ }
+}
+
+int sys_getsockopt(int fd, int layer, int number,
+ void *__restrict buffer, socklen_t *__restrict size) {
+ SignalGuard sguard;
+
+ if(layer == SOL_SOCKET && number == SO_PEERCRED) {
+ if(*size != sizeof(struct ucred))
+ return EINVAL;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_GET_OPTION);
+ req.set_command(SO_PEERCRED);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ struct ucred creds;
+ creds.pid = resp.pid();
+ creds.uid = resp.uid();
+ creds.gid = resp.gid();
+ memcpy(buffer, &creds, sizeof(struct ucred));
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_SNDBUF) {
+ mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_SNDBUF is unimplemented\e[39m" << frg::endlog;
+ *(int *)buffer = 4096;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_TYPE) {
+ mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_TYPE is unimplemented, hardcoding SOCK_STREAM\e[39m" << frg::endlog;
+ *(int *)buffer = SOCK_STREAM;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_ERROR) {
+ mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_ERROR is unimplemented, hardcoding 0\e[39m" << frg::endlog;
+ *(int *)buffer = 0;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_KEEPALIVE) {
+ mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_KEEPALIVE is unimplemented, hardcoding 0\e[39m" << frg::endlog;
+ *(int *)buffer = 0;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_LINGER) {
+ mlibc::infoLogger() << "\e[31mmlibc: getsockopt() call with SOL_SOCKET and SO_LINGER is unimplemented, hardcoding 0\e[39m" << frg::endlog;
+ *(int *)buffer = 0;
+ return 0;
+ }else{
+ mlibc::panicLogger() << "\e[31mmlibc: Unexpected getsockopt() call, layer: " << layer << " number: " << number << "\e[39m" << frg::endlog;
+ __builtin_unreachable();
+ }
+}
+
+int sys_setsockopt(int fd, int layer, int number,
+ const void *buffer, socklen_t size) {
+ SignalGuard sguard;
+
+ if(layer == SOL_SOCKET && number == SO_PASSCRED) {
+ int value;
+ __ensure(size == sizeof(int));
+ memcpy(&value, buffer, sizeof(int));
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_SET_OPTION);
+ req.set_command(SO_PASSCRED);
+ req.set_value(value);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_ATTACH_FILTER) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt(SO_ATTACH_FILTER) is not implemented"
+ " correctly\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_RCVBUFFORCE) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt(SO_RCVBUFFORCE) is not implemented"
+ " correctly\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_SNDBUF) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_SNDBUF is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_KEEPALIVE) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_KEEPALIVE is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_REUSEADDR) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_REUSEADDR is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_REUSEPORT) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_REUSEPORT is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_RCVBUF) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_RCVBUF is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == IPPROTO_TCP && number == TCP_NODELAY) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_NODELAY is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_SOCKET && number == SO_ACCEPTCONN) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_SOCKET and SO_ACCEPTCONN is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == IPPROTO_TCP && number == TCP_KEEPIDLE) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_KEEPIDLE is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_NETLINK && number == NETLINK_EXT_ACK) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_NETLINK and NETLINK_EXT_ACK is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == SOL_NETLINK && number == NETLINK_GET_STRICT_CHK) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with SOL_NETLINK and NETLINK_EXT_ACK is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == IPPROTO_TCP && number == TCP_KEEPINTVL) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_KEEPINTVL is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else if(layer == IPPROTO_TCP && number == TCP_KEEPCNT) {
+ mlibc::infoLogger() << "\e[31mmlibc: setsockopt() call with IPPROTO_TCP and TCP_KEEPCNT is unimplemented\e[39m" << frg::endlog;
+ return 0;
+ }else{
+ mlibc::panicLogger() << "\e[31mmlibc: Unexpected setsockopt() call, layer: " << layer << " number: " << number << "\e[39m" << frg::endlog;
+ __builtin_unreachable();
+ }
+}
+
+int sys_listen(int fd, int) {
+ SignalGuard sguard;
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_req_type(managarm::fs::CntReqType::PT_LISTEN);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ return 0;
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/generic/time.cpp b/lib/mlibc/sysdeps/managarm/generic/time.cpp
new file mode 100644
index 0000000..468a738
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/time.cpp
@@ -0,0 +1,81 @@
+
+#include <bits/ensure.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <hel.h>
+#include <hel-syscalls.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/posix-pipe.hpp>
+#include <mlibc/all-sysdeps.hpp>
+
+struct TrackerPage {
+ uint64_t seqlock;
+ int32_t state;
+ int32_t padding;
+ int64_t refClock;
+ int64_t baseRealtime;
+};
+
+extern thread_local TrackerPage *__mlibc_clk_tracker_page;
+
+namespace mlibc {
+
+int sys_clock_get(int clock, time_t *secs, long *nanos) {
+ // This implementation is inherently signal-safe.
+ if(clock == CLOCK_MONOTONIC || clock == CLOCK_MONOTONIC_RAW || clock == CLOCK_MONOTONIC_COARSE) {
+ uint64_t tick;
+ HEL_CHECK(helGetClock(&tick));
+ *secs = tick / 1000000000;
+ *nanos = tick % 1000000000;
+ }else if(clock == CLOCK_REALTIME) {
+ cacheFileTable();
+
+ // Start the seqlock read.
+ auto seqlock = __atomic_load_n(&__mlibc_clk_tracker_page->seqlock, __ATOMIC_ACQUIRE);
+ __ensure(!(seqlock & 1));
+
+ // Perform the actual loads.
+ auto ref = __atomic_load_n(&__mlibc_clk_tracker_page->refClock, __ATOMIC_RELAXED);
+ auto base = __atomic_load_n(&__mlibc_clk_tracker_page->baseRealtime, __ATOMIC_RELAXED);
+
+ // Finish the seqlock read.
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+ __ensure(__atomic_load_n(&__mlibc_clk_tracker_page->seqlock, __ATOMIC_RELAXED) == seqlock);
+
+ // Calculate the current time.
+ uint64_t tick;
+ HEL_CHECK(helGetClock(&tick));
+ __ensure(tick >= (uint64_t)__mlibc_clk_tracker_page->refClock); // TODO: Respect the seqlock!
+ tick -= ref;
+ tick += base;
+ *secs = tick / 1000000000;
+ *nanos = tick % 1000000000;
+ }else if(clock == CLOCK_PROCESS_CPUTIME_ID) {
+ mlibc::infoLogger() << "\e[31mmlibc: clock_gettime does not support the CPU time clocks"
+ "\e[39m" << frg::endlog;
+ *secs = 0;
+ *nanos = 0;
+ }else if(clock == CLOCK_BOOTTIME) {
+ mlibc::infoLogger() << "\e[31mmlibc: clock_gettime does not support CLOCK_BOOTTIME"
+ "\e[39m" << frg::endlog;
+ *secs = 0;
+ *nanos = 0;
+ }else{
+ mlibc::panicLogger() << "mlibc: Unexpected clock " << clock << frg::endlog;
+ }
+ return 0;
+}
+
+int sys_clock_getres(int clock, time_t *secs, long *nanos) {
+ (void)clock;
+ (void)secs;
+ (void)nanos;
+ mlibc::infoLogger() << "mlibc: clock_getres is a stub" << frg::endlog;
+ return 0;
+}
+
+} //namespace mlibc
+
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/access.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/access.h
new file mode 120000
index 0000000..cb83931
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/access.h
@@ -0,0 +1 @@
+../../../../abis/linux/access.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/auxv.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/auxv.h
new file mode 120000
index 0000000..e760d62
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/auxv.h
@@ -0,0 +1 @@
+../../../../abis/managarm/auxv.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/blkcnt_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/blkcnt_t.h
new file mode 120000
index 0000000..0b0ec27
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/blkcnt_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/blkcnt_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/blksize_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/blksize_t.h
new file mode 120000
index 0000000..7dc8d7c
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/blksize_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/blksize_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/clockid_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/clockid_t.h
new file mode 120000
index 0000000..6a42da5
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/clockid_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/clockid_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/dev_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/dev_t.h
new file mode 120000
index 0000000..bca881e
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/dev_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/dev_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/epoll.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/epoll.h
new file mode 120000
index 0000000..eb4b76d
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/epoll.h
@@ -0,0 +1 @@
+../../../../abis/linux/epoll.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/errno.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/errno.h
new file mode 120000
index 0000000..6e507de
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/errno.h
@@ -0,0 +1 @@
+../../../../abis/linux/errno.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/fcntl.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/fcntl.h
new file mode 120000
index 0000000..463e2c9
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/fcntl.h
@@ -0,0 +1 @@
+../../../../abis/linux/fcntl.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/fsblkcnt_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/fsblkcnt_t.h
new file mode 120000
index 0000000..898dfb2
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/fsblkcnt_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/fsblkcnt_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/fsfilcnt_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/fsfilcnt_t.h
new file mode 120000
index 0000000..791755c
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/fsfilcnt_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/fsfilcnt_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/gid_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/gid_t.h
new file mode 120000
index 0000000..abce6d6
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/gid_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/gid_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/in.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/in.h
new file mode 120000
index 0000000..418d1d5
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/in.h
@@ -0,0 +1 @@
+../../../../abis/linux/in.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/ino_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/ino_t.h
new file mode 120000
index 0000000..4c20aca
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/ino_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/ino_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/inotify.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/inotify.h
new file mode 120000
index 0000000..b5cb282
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/inotify.h
@@ -0,0 +1 @@
+../../../../abis/linux/inotify.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/ioctls.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/ioctls.h
new file mode 120000
index 0000000..595106b
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/ioctls.h
@@ -0,0 +1 @@
+../../../../abis/linux/ioctls.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/limits.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/limits.h
new file mode 120000
index 0000000..6c88db2
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/limits.h
@@ -0,0 +1 @@
+../../../../abis/linux/limits.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/mode_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/mode_t.h
new file mode 120000
index 0000000..5d78fdf
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/mode_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/mode_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/mqueue.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/mqueue.h
new file mode 120000
index 0000000..fa87b07
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/mqueue.h
@@ -0,0 +1 @@
+../../../../abis/linux/mqueue.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/msg.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/msg.h
new file mode 120000
index 0000000..f402b49
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/msg.h
@@ -0,0 +1 @@
+../../../../abis/linux/msg.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/nlink_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/nlink_t.h
new file mode 120000
index 0000000..bb3b625
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/nlink_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/nlink_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/packet.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/packet.h
new file mode 120000
index 0000000..998ef1a
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/packet.h
@@ -0,0 +1 @@
+../../../../abis/linux/packet.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/pid_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/pid_t.h
new file mode 120000
index 0000000..baa90f6
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/pid_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/pid_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/poll.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/poll.h
new file mode 120000
index 0000000..8ea6a0a
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/poll.h
@@ -0,0 +1 @@
+../../../../abis/linux/poll.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/ptrace.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/ptrace.h
new file mode 120000
index 0000000..b2517b2
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/ptrace.h
@@ -0,0 +1 @@
+../../../../abis/linux/ptrace.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/reboot.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/reboot.h
new file mode 120000
index 0000000..77013a4
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/reboot.h
@@ -0,0 +1 @@
+../../../../abis/linux/reboot.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/resource.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/resource.h
new file mode 120000
index 0000000..88d7402
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/resource.h
@@ -0,0 +1 @@
+../../../../abis/linux/resource.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/seek-whence.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/seek-whence.h
new file mode 120000
index 0000000..df7bccf
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/seek-whence.h
@@ -0,0 +1 @@
+../../../../abis/linux/seek-whence.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/shm.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/shm.h
new file mode 120000
index 0000000..067d8c4
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/shm.h
@@ -0,0 +1 @@
+../../../../abis/linux/shm.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/signal.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/signal.h
new file mode 120000
index 0000000..4dcb0b7
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/signal.h
@@ -0,0 +1 @@
+../../../../abis/linux/signal.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/socket.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/socket.h
new file mode 120000
index 0000000..f1dc016
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/socket.h
@@ -0,0 +1 @@
+../../../../abis/linux/socket.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/socklen_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/socklen_t.h
new file mode 120000
index 0000000..41f3b11
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/socklen_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/socklen_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/stat.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/stat.h
new file mode 120000
index 0000000..1f63b41
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/stat.h
@@ -0,0 +1 @@
+../../../../abis/linux/stat.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/statfs.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/statfs.h
new file mode 120000
index 0000000..e3d202f
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/statfs.h
@@ -0,0 +1 @@
+../../../../abis/linux/statfs.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/statvfs.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/statvfs.h
new file mode 120000
index 0000000..1fc80c2
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/statvfs.h
@@ -0,0 +1 @@
+../../../../abis/linux/statvfs.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/suseconds_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/suseconds_t.h
new file mode 120000
index 0000000..9ed6597
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/suseconds_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/suseconds_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/termios.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/termios.h
new file mode 120000
index 0000000..ee8f0b0
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/termios.h
@@ -0,0 +1 @@
+../../../../abis/linux/termios.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/time.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/time.h
new file mode 120000
index 0000000..2a02625
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/time.h
@@ -0,0 +1 @@
+../../../../abis/linux/time.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/uid_t.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/uid_t.h
new file mode 120000
index 0000000..b306777
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/uid_t.h
@@ -0,0 +1 @@
+../../../../abis/linux/uid_t.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/utsname.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/utsname.h
new file mode 120000
index 0000000..b285754
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/utsname.h
@@ -0,0 +1 @@
+../../../../abis/linux/utsname.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/vm-flags.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/vm-flags.h
new file mode 120000
index 0000000..bbe258c
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/vm-flags.h
@@ -0,0 +1 @@
+../../../../abis/linux/vm-flags.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/vt.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/vt.h
new file mode 120000
index 0000000..5798a4a
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/vt.h
@@ -0,0 +1 @@
+../../../../abis/linux/vt.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/wait.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/wait.h
new file mode 120000
index 0000000..feb2840
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/wait.h
@@ -0,0 +1 @@
+../../../../abis/linux/wait.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/abi-bits/xattr.h b/lib/mlibc/sysdeps/managarm/include/abi-bits/xattr.h
new file mode 120000
index 0000000..66412d7
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/abi-bits/xattr.h
@@ -0,0 +1 @@
+../../../../abis/linux/xattr.h \ No newline at end of file
diff --git a/lib/mlibc/sysdeps/managarm/include/mlibc/posix-pipe.hpp b/lib/mlibc/sysdeps/managarm/include/mlibc/posix-pipe.hpp
new file mode 100644
index 0000000..4bcf1cb
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/mlibc/posix-pipe.hpp
@@ -0,0 +1,300 @@
+#ifndef MLIBC_POSIX_PIPE
+#define MLIBC_POSIX_PIPE
+
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <cstddef>
+
+#include <hel.h>
+#include <hel-syscalls.h>
+
+struct SignalGuard {
+ SignalGuard();
+
+ SignalGuard(const SignalGuard &) = delete;
+
+ ~SignalGuard();
+
+ SignalGuard &operator= (const SignalGuard &) = delete;
+};
+
+// We need an allocator for message structs in sysdeps functions; the "normal" mlibc
+// allocator cannot be used, as the sysdeps function might be called from a signal.
+MemoryAllocator &getSysdepsAllocator();
+
+struct Queue;
+
+struct ElementHandle {
+ friend void swap(ElementHandle &u, ElementHandle &v) {
+ using std::swap;
+ swap(u._queue, v._queue);
+ swap(u._n, v._n);
+ swap(u._data, v._data);
+ }
+
+ ElementHandle()
+ : _queue{nullptr}, _n{-1}, _data{nullptr} { }
+
+ ElementHandle(Queue *queue, int n, void *data)
+ : _queue{queue}, _n{n}, _data{data} { }
+
+ ElementHandle(const ElementHandle &other);
+
+ ElementHandle(ElementHandle &&other)
+ : ElementHandle{} {
+ swap(*this, other);
+ }
+
+ ~ElementHandle();
+
+ ElementHandle &operator= (ElementHandle other) {
+ swap(*this, other);
+ return *this;
+ }
+
+ void *data() {
+ return _data;
+ }
+
+ void advance(size_t size) {
+ _data = reinterpret_cast<char *>(_data) + size;
+ }
+
+private:
+ Queue *_queue;
+ int _n;
+ void *_data;
+};
+
+struct Queue {
+ Queue()
+ : _handle{kHelNullHandle} {
+ // We do not need to protect those allocations against signals as this constructor
+ // is only called during library initialization.
+ _chunks[0] = reinterpret_cast<HelChunk *>(getSysdepsAllocator().allocate(sizeof(HelChunk) + 4096));
+ _chunks[1] = reinterpret_cast<HelChunk *>(getSysdepsAllocator().allocate(sizeof(HelChunk) + 4096));
+
+ recreateQueue();
+ }
+
+ Queue(const Queue &) = delete;
+
+ Queue &operator= (const Queue &) = delete;
+
+ void recreateQueue() {
+ // Reset the internal queue state.
+ _retrieveIndex = 0;
+ _nextIndex = 0;
+ _lastProgress = 0;
+
+ // Setup the queue header.
+ HelQueueParameters params {
+ .flags = 0,
+ .ringShift = 1,
+ .numChunks = 2,
+ .chunkSize = 4096
+ };
+ HEL_CHECK(helCreateQueue(&params, &_handle));
+
+ auto chunksOffset = (sizeof(HelQueue) + (sizeof(int) << 1) + 63) & ~size_t(63);
+ auto reservedPerChunk = (sizeof(HelChunk) + params.chunkSize + 63) & ~size_t(63);
+ auto overallSize = chunksOffset + params.numChunks * reservedPerChunk;
+
+ void *mapping;
+ HEL_CHECK(helMapMemory(_handle, kHelNullHandle, nullptr,
+ 0, (overallSize + 0xFFF) & ~size_t(0xFFF),
+ kHelMapProtRead | kHelMapProtWrite, &mapping));
+
+ _queue = reinterpret_cast<HelQueue *>(mapping);
+ auto chunksPtr = reinterpret_cast<std::byte *>(mapping) + chunksOffset;
+ for(unsigned int i = 0; i < 2; ++i)
+ _chunks[i] = reinterpret_cast<HelChunk *>(chunksPtr + i * reservedPerChunk);
+
+ // Reset and enqueue the chunks.
+ _chunks[0]->progressFutex = 0;
+ _chunks[1]->progressFutex = 0;
+ _refCount[0] = 1;
+ _refCount[1] = 1;
+
+ _queue->indexQueue[0] = 0;
+ _queue->indexQueue[1] = 1;
+ _queue->headFutex = 0;
+ _nextIndex = 2;
+ _wakeHeadFutex();
+ }
+
+ HelHandle getQueue() {
+ return _handle;
+ }
+
+ void trim() { }
+
+ ElementHandle dequeueSingle() {
+ while(true) {
+ __ensure(_retrieveIndex != _nextIndex);
+
+ bool done;
+ _waitProgressFutex(&done);
+
+ auto n = _numberOf(_retrieveIndex);
+ __ensure(_refCount[n]);
+
+ if(done) {
+ retire(n);
+
+ _lastProgress = 0;
+ _retrieveIndex = ((_retrieveIndex + 1) & kHelHeadMask);
+ continue;
+ }
+
+ // Dequeue the next element.
+ auto ptr = (char *)_chunks[n] + sizeof(HelChunk) + _lastProgress;
+ auto element = reinterpret_cast<HelElement *>(ptr);
+ _lastProgress += sizeof(HelElement) + element->length;
+ _refCount[n]++;
+ return ElementHandle{this, n, ptr + sizeof(HelElement)};
+ }
+ }
+
+ void retire(int n) {
+ __ensure(_refCount[n]);
+ if(_refCount[n]-- > 1)
+ return;
+
+ // Reset and enqueue the chunk again.
+ _chunks[n]->progressFutex = 0;
+ _refCount[n] = 1;
+
+ _queue->indexQueue[_nextIndex & 1] = n;
+ _nextIndex = ((_nextIndex + 1) & kHelHeadMask);
+ _wakeHeadFutex();
+ }
+
+ void reference(int n) {
+ _refCount[n]++;
+ }
+
+private:
+ int _numberOf(int index) {
+ return _queue->indexQueue[index & 1];
+ }
+
+ HelChunk *_retrieveChunk() {
+ return _chunks[_numberOf(_retrieveIndex)];
+ }
+
+ void _wakeHeadFutex() {
+ auto futex = __atomic_exchange_n(&_queue->headFutex, _nextIndex, __ATOMIC_RELEASE);
+ if(futex & kHelHeadWaiters)
+ HEL_CHECK(helFutexWake(&_queue->headFutex));
+ }
+
+ void _waitProgressFutex(bool *done) {
+ while(true) {
+ auto futex = __atomic_load_n(&_retrieveChunk()->progressFutex, __ATOMIC_ACQUIRE);
+ __ensure(!(futex & ~(kHelProgressMask | kHelProgressWaiters | kHelProgressDone)));
+ do {
+ if(_lastProgress != (futex & kHelProgressMask)) {
+ *done = false;
+ return;
+ }else if(futex & kHelProgressDone) {
+ *done = true;
+ return;
+ }
+
+ if(futex & kHelProgressWaiters)
+ break; // Waiters bit is already set (in a previous iteration).
+ } while(!__atomic_compare_exchange_n(&_retrieveChunk()->progressFutex, &futex,
+ _lastProgress | kHelProgressWaiters,
+ false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE));
+
+ HEL_CHECK(helFutexWait(&_retrieveChunk()->progressFutex,
+ _lastProgress | kHelProgressWaiters, -1));
+ }
+ }
+
+private:
+ HelHandle _handle;
+ HelQueue *_queue;
+ HelChunk *_chunks[2];
+
+ // Index of the chunk that we are currently retrieving/inserting next.
+ int _retrieveIndex;
+ int _nextIndex;
+
+ // Progress into the current chunk.
+ int _lastProgress;
+
+ // Number of ElementHandle objects alive.
+ int _refCount[2];
+};
+
+inline ElementHandle::~ElementHandle() {
+ if(_queue)
+ _queue->retire(_n);
+}
+
+inline ElementHandle::ElementHandle(const ElementHandle &other) {
+ _queue = other._queue;
+ _n = other._n;
+ _data = other._data;
+
+ _queue->reference(_n);
+}
+
+
+inline HelSimpleResult *parseSimple(ElementHandle &element) {
+ auto result = reinterpret_cast<HelSimpleResult *>(element.data());
+ element.advance(sizeof(HelSimpleResult));
+ return result;
+}
+
+inline HelInlineResult *parseInline(ElementHandle &element) {
+ auto result = reinterpret_cast<HelInlineResult *>(element.data());
+ element.advance(sizeof(HelInlineResult) + ((result->length + 7) & ~size_t(7)));
+ return result;
+}
+
+inline HelLengthResult *parseLength(ElementHandle &element) {
+ auto result = reinterpret_cast<HelLengthResult *>(element.data());
+ element.advance(sizeof(HelLengthResult));
+ return result;
+}
+
+inline HelHandleResult *parseHandle(ElementHandle &element) {
+ auto result = reinterpret_cast<HelHandleResult *>(element.data());
+ element.advance(sizeof(HelHandleResult));
+ return result;
+}
+
+HelHandle getPosixLane();
+HelHandle *cacheFileTable();
+HelHandle getHandleForFd(int fd);
+void clearCachedInfos();
+
+extern thread_local Queue globalQueue;
+
+// This include is here because it needs ElementHandle to be declared
+#include <helix/ipc-structs.hpp>
+
+template <typename ...Args>
+auto exchangeMsgsSync(HelHandle descriptor, Args &&...args) {
+ auto results = helix_ng::createResultsTuple(args...);
+ auto actions = helix_ng::chainActionArrays(args...);
+
+ HEL_CHECK(helSubmitAsync(descriptor, actions.data(),
+ actions.size(), globalQueue.getQueue(), 0, 0));
+
+ auto element = globalQueue.dequeueSingle();
+ void *ptr = element.data();
+
+ [&]<size_t ...p>(std::index_sequence<p...>) {
+ (results.template get<p>().parse(ptr, element), ...);
+ } (std::make_index_sequence<std::tuple_size_v<decltype(results)>>{});
+
+ return results;
+}
+
+
+#endif // MLIBC_POSIX_PIPE
diff --git a/lib/mlibc/sysdeps/managarm/include/mlibc/thread-entry.hpp b/lib/mlibc/sysdeps/managarm/include/mlibc/thread-entry.hpp
new file mode 100644
index 0000000..2dd88a6
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/include/mlibc/thread-entry.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <mlibc/tcb.hpp>
+
+extern "C" void __mlibc_start_thread(void);
+extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb);
+
+namespace mlibc {
+ void *prepare_stack(void *entry, void *user_arg, void *tcb);
+}
diff --git a/lib/mlibc/sysdeps/managarm/meson.build b/lib/mlibc/sysdeps/managarm/meson.build
new file mode 100644
index 0000000..3b85e30
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/meson.build
@@ -0,0 +1,145 @@
+
+bragi = find_program('bragi')
+
+bragi_gen = generator(bragi, arguments: [
+ '-l', 'frigg',
+ '--protobuf',
+ '@INPUT@',
+ '@OUTPUT@'
+ ],
+ output: '@BASENAME@.frigg_bragi.hpp')
+
+fs_bragi = bragi_gen.process('../../subprojects/managarm/protocols/fs/fs.bragi')
+posix_bragi = bragi_gen.process('../../subprojects/managarm/protocols/posix/posix.bragi')
+
+managarm_incl = include_directories(
+ '../../subprojects/managarm/protocols/posix/include',
+ '../../subprojects/managarm/hel/include',
+ '../../subprojects/bragi/include')
+
+rtdl_include_dirs += managarm_incl
+rtdl_sources += files(
+ 'generic/ensure.cpp',
+ 'generic/memory.cpp',
+ 'rtdl-generic/support.cpp',
+)
+rtdl_sources += [
+ fs_bragi,
+ posix_bragi,
+]
+
+libc_include_dirs += include_directories('../../../ports/libdrm/include')
+libc_include_dirs += managarm_incl
+
+libc_sources += files(
+ 'generic/drm.cpp',
+ 'generic/ensure.cpp',
+ 'generic/entry.cpp',
+ 'generic/file.cpp',
+ 'generic/fork-exec.cpp',
+ 'generic/ioctl.cpp',
+ 'generic/memory.cpp',
+ 'generic/mount.cpp',
+ 'generic/net.cpp',
+ 'generic/sched.cpp',
+ 'generic/signals.cpp',
+ 'generic/socket.cpp',
+ 'generic/time.cpp'
+)
+libc_sources += [
+ fs_bragi,
+ posix_bragi,
+]
+
+if host_machine.cpu_family() == 'aarch64'
+ libc_sources += files(
+ 'aarch64/signals.S',
+ 'aarch64/thread_entry.S',
+ 'aarch64/thread.cpp'
+ )
+elif host_machine.cpu_family() == 'x86_64'
+ libc_sources += files(
+ 'x86_64/signals.S',
+ 'x86_64/thread_entry.S',
+ 'x86_64/thread.cpp'
+ )
+else
+ error('Unknown architecture')
+endif
+
+if not no_headers
+ install_headers(
+ 'include/abi-bits/access.h',
+ 'include/abi-bits/auxv.h',
+ 'include/abi-bits/seek-whence.h',
+ 'include/abi-bits/vm-flags.h',
+ 'include/abi-bits/errno.h',
+ 'include/abi-bits/fcntl.h',
+ 'include/abi-bits/in.h',
+ 'include/abi-bits/stat.h',
+ 'include/abi-bits/signal.h',
+ 'include/abi-bits/reboot.h',
+ 'include/abi-bits/resource.h',
+ 'include/abi-bits/socket.h',
+ 'include/abi-bits/termios.h',
+ 'include/abi-bits/time.h',
+ 'include/abi-bits/blkcnt_t.h',
+ 'include/abi-bits/blksize_t.h',
+ 'include/abi-bits/dev_t.h',
+ 'include/abi-bits/gid_t.h',
+ 'include/abi-bits/ino_t.h',
+ 'include/abi-bits/mode_t.h',
+ 'include/abi-bits/nlink_t.h',
+ 'include/abi-bits/pid_t.h',
+ 'include/abi-bits/uid_t.h',
+ 'include/abi-bits/wait.h',
+ 'include/abi-bits/limits.h',
+ 'include/abi-bits/utsname.h',
+ 'include/abi-bits/ptrace.h',
+ 'include/abi-bits/poll.h',
+ 'include/abi-bits/epoll.h',
+ 'include/abi-bits/packet.h',
+ 'include/abi-bits/inotify.h',
+ 'include/abi-bits/clockid_t.h',
+ 'include/abi-bits/shm.h',
+ 'include/abi-bits/mqueue.h',
+ 'include/abi-bits/suseconds_t.h',
+ 'include/abi-bits/fsfilcnt_t.h',
+ 'include/abi-bits/fsblkcnt_t.h',
+ 'include/abi-bits/socklen_t.h',
+ 'include/abi-bits/statfs.h',
+ 'include/abi-bits/statvfs.h',
+ 'include/abi-bits/ioctls.h',
+ 'include/abi-bits/xattr.h',
+ 'include/abi-bits/msg.h',
+ 'include/abi-bits/vt.h',
+ subdir: 'abi-bits',
+ follow_symlinks: true
+ )
+endif
+
+if not headers_only
+ crtstuff = ['crt0']
+ if host_machine.cpu_family() in ['x86_64', 'aarch64']
+ crtstuff += [
+ 'Scrt1',
+ 'crti',
+ 'crtn'
+ ]
+ endif
+ foreach crtthing : crtstuff
+ crtf = crtthing + '.S'
+ crt_src = files(host_machine.cpu_family() / 'crt-src' / crtf)
+ crt = custom_target(
+ crtthing,
+ build_by_default: true,
+ command: c_compiler.cmd_array() + ['-c', '-o', '@OUTPUT@', '@INPUT@'],
+ input: crt_src,
+ output: crtthing + '.o',
+ install: true,
+ install_dir: get_option('libdir')
+ )
+ endforeach
+
+endif
+
diff --git a/lib/mlibc/sysdeps/managarm/rtdl-generic/support.cpp b/lib/mlibc/sysdeps/managarm/rtdl-generic/support.cpp
new file mode 100644
index 0000000..ee8ecbb
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/rtdl-generic/support.cpp
@@ -0,0 +1,444 @@
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <cstddef>
+
+#include <frg/manual_box.hpp>
+#include <frg/string.hpp>
+#include <hel.h>
+#include <hel-syscalls.h>
+
+#include <mlibc/allocator.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <posix.frigg_bragi.hpp>
+#include <fs.frigg_bragi.hpp>
+
+#include <protocols/posix/data.hpp>
+#include <protocols/posix/supercalls.hpp>
+
+// --------------------------------------------------------
+// POSIX I/O functions.
+// --------------------------------------------------------
+
+HelHandle posixLane;
+HelHandle *fileTable;
+
+void cacheFileTable() {
+ if(fileTable)
+ return;
+
+ posix::ManagarmProcessData data;
+ HEL_CHECK(helSyscall1(kHelCallSuper + posix::superGetProcessData, reinterpret_cast<HelWord>(&data)));
+ posixLane = data.posixLane;
+ fileTable = data.fileTable;
+}
+
+template<typename T>
+T load(void *ptr) {
+ T result;
+ memcpy(&result, ptr, sizeof(T));
+ return result;
+}
+
+// This Queue implementation is more simplistic than the ones in mlibc and helix.
+// In fact, we only manage a single chunk; this minimizes the memory usage of the queue.
+struct Queue {
+ Queue()
+ : _handle{kHelNullHandle}, _lastProgress(0) {
+ HelQueueParameters params {
+ .flags = 0,
+ .ringShift = 0,
+ .numChunks = 1,
+ .chunkSize = 4096
+ };
+ HEL_CHECK(helCreateQueue(&params, &_handle));
+
+ auto chunksOffset = (sizeof(HelQueue) + (sizeof(int) << 0) + 63) & ~size_t(63);
+ auto reservedPerChunk = (sizeof(HelChunk) + params.chunkSize + 63) & ~size_t(63);
+ auto overallSize = chunksOffset + params.numChunks * reservedPerChunk;
+
+ void *mapping;
+ HEL_CHECK(helMapMemory(_handle, kHelNullHandle, nullptr,
+ 0, (overallSize + 0xFFF) & ~size_t(0xFFF),
+ kHelMapProtRead | kHelMapProtWrite, &mapping));
+
+ _queue = reinterpret_cast<HelQueue *>(mapping);
+ _chunk = reinterpret_cast<HelChunk *>(
+ reinterpret_cast<std::byte *>(mapping) + chunksOffset);
+
+ // Reset and enqueue the first chunk.
+ _chunk->progressFutex = 0;
+
+ _queue->indexQueue[0] = 0;
+ _queue->headFutex = 1;
+ _nextIndex = 1;
+ _wakeHeadFutex();
+ }
+
+ Queue(const Queue &) = delete;
+
+ Queue &operator= (const Queue &) = delete;
+
+ HelHandle getHandle() {
+ return _handle;
+ }
+
+ void *dequeueSingle() {
+ while(true) {
+ bool done;
+ _waitProgressFutex(&done);
+ if(done) {
+ // Reset and enqueue the chunk again.
+ _chunk->progressFutex = 0;
+
+ _queue->indexQueue[0] = 0;
+ _nextIndex = ((_nextIndex + 1) & kHelHeadMask);
+ _wakeHeadFutex();
+
+ _lastProgress = 0;
+ continue;
+ }
+
+ // Dequeue the next element.
+ auto ptr = (char *)_chunk + sizeof(HelChunk) + _lastProgress;
+ auto element = load<HelElement>(ptr);
+ _lastProgress += sizeof(HelElement) + element.length;
+ return ptr + sizeof(HelElement);
+ }
+ }
+
+private:
+ void _wakeHeadFutex() {
+ auto futex = __atomic_exchange_n(&_queue->headFutex, _nextIndex, __ATOMIC_RELEASE);
+ if(futex & kHelHeadWaiters)
+ HEL_CHECK(helFutexWake(&_queue->headFutex));
+ }
+
+ void _waitProgressFutex(bool *done) {
+ while(true) {
+ auto futex = __atomic_load_n(&_chunk->progressFutex, __ATOMIC_ACQUIRE);
+ __ensure(!(futex & ~(kHelProgressMask | kHelProgressWaiters | kHelProgressDone)));
+ do {
+ if(_lastProgress != (futex & kHelProgressMask)) {
+ *done = false;
+ return;
+ }else if(futex & kHelProgressDone) {
+ *done = true;
+ return;
+ }
+
+ if(futex & kHelProgressWaiters)
+ break; // Waiters bit is already set (in a previous iteration).
+ } while(!__atomic_compare_exchange_n(&_chunk->progressFutex, &futex,
+ _lastProgress | kHelProgressWaiters,
+ false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE));
+
+ HEL_CHECK(helFutexWait(&_chunk->progressFutex,
+ _lastProgress | kHelProgressWaiters, -1));
+ }
+ }
+
+private:
+ HelHandle _handle;
+ HelQueue *_queue;
+ HelChunk *_chunk;
+ int _nextIndex;
+ int _lastProgress;
+};
+
+frg::manual_box<Queue> globalQueue;
+
+HelSimpleResult *parseSimple(void *&element) {
+ auto result = reinterpret_cast<HelSimpleResult *>(element);
+ element = (char *)element + sizeof(HelSimpleResult);
+ return result;
+}
+
+HelInlineResult *parseInline(void *&element) {
+ auto result = reinterpret_cast<HelInlineResult *>(element);
+ element = (char *)element + sizeof(HelInlineResult)
+ + ((result->length + 7) & ~size_t(7));
+ return result;
+}
+
+HelLengthResult *parseLength(void *&element) {
+ auto result = reinterpret_cast<HelLengthResult *>(element);
+ element = (char *)element + sizeof(HelLengthResult);
+ return result;
+}
+
+HelHandleResult *parseHandle(void *&element) {
+ auto result = reinterpret_cast<HelHandleResult *>(element);
+ element = (char *)element + sizeof(HelHandleResult);
+ return result;
+}
+
+namespace mlibc {
+
+int sys_tcb_set(void *pointer) {
+#if defined(__aarch64__)
+ asm volatile ("msr tpidr_el0, %0" :: "r"(pointer));
+#else
+ HEL_CHECK(helWriteFsBase(pointer));
+#endif
+ return 0;
+}
+
+int sys_open(const char *path, int flags, mode_t mode, int *fd) {
+ cacheFileTable();
+ HelAction actions[4];
+
+ managarm::posix::OpenAtRequest<MemoryAllocator> req(getAllocator());
+ req.set_fd(AT_FDCWD);
+ req.set_flags(flags);
+ req.set_mode(mode);
+ req.set_path(frg::string<MemoryAllocator>(getAllocator(), path));
+
+ if(!globalQueue.valid())
+ globalQueue.initialize();
+
+ frg::string<MemoryAllocator> head(getAllocator());
+ frg::string<MemoryAllocator> tail(getAllocator());
+ head.resize(req.size_of_head());
+ tail.resize(req.size_of_tail());
+ bragi::limited_writer headWriter{head.data(), head.size()};
+ bragi::limited_writer tailWriter{tail.data(), tail.size()};
+ auto headOk = req.encode_head(headWriter);
+ auto tailOk = req.encode_tail(tailWriter);
+ __ensure(headOk);
+ __ensure(tailOk);
+
+ actions[0].type = kHelActionOffer;
+ actions[0].flags = kHelItemAncillary;
+ actions[1].type = kHelActionSendFromBuffer;
+ actions[1].flags = kHelItemChain;
+ actions[1].buffer = head.data();
+ actions[1].length = head.size();
+ actions[2].type = kHelActionSendFromBuffer;
+ actions[2].flags = kHelItemChain;
+ actions[2].buffer = tail.data();
+ actions[2].length = tail.size();
+ actions[3].type = kHelActionRecvInline;
+ actions[3].flags = 0;
+ HEL_CHECK(helSubmitAsync(posixLane, actions, 4, globalQueue->getHandle(), 0, 0));
+
+ auto element = globalQueue->dequeueSingle();
+ auto offer = parseHandle(element);
+ auto send_head = parseSimple(element);
+ auto send_tail = parseSimple(element);
+ auto recv_resp = parseInline(element);
+ HEL_CHECK(offer->error);
+ HEL_CHECK(send_head->error);
+ HEL_CHECK(send_tail->error);
+ HEL_CHECK(recv_resp->error);
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getAllocator());
+ resp.ParseFromArray(recv_resp->data, recv_resp->length);
+
+ if(resp.error() == managarm::posix::Errors::FILE_NOT_FOUND)
+ return -1;
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *fd = resp.fd();
+ return 0;
+}
+
+int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) {
+ __ensure(whence == SEEK_SET);
+
+ cacheFileTable();
+ auto lane = fileTable[fd];
+ HelAction actions[3];
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getAllocator());
+ req.set_req_type(managarm::fs::CntReqType::SEEK_ABS);
+ req.set_rel_offset(offset);
+
+ if(!globalQueue.valid())
+ globalQueue.initialize();
+
+ frg::string<MemoryAllocator> ser(getAllocator());
+ req.SerializeToString(&ser);
+ actions[0].type = kHelActionOffer;
+ actions[0].flags = kHelItemAncillary;
+ actions[1].type = kHelActionSendFromBuffer;
+ actions[1].flags = kHelItemChain;
+ actions[1].buffer = ser.data();
+ actions[1].length = ser.size();
+ actions[2].type = kHelActionRecvInline;
+ actions[2].flags = 0;
+ HEL_CHECK(helSubmitAsync(lane, actions, 3, globalQueue->getHandle(), 0, 0));
+
+ auto element = globalQueue->dequeueSingle();
+ auto offer = parseHandle(element);
+ auto send_req = parseSimple(element);
+ auto recv_resp = parseInline(element);
+ HEL_CHECK(offer->error);
+ HEL_CHECK(send_req->error);
+ HEL_CHECK(recv_resp->error);
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getAllocator());
+ resp.ParseFromArray(recv_resp->data, recv_resp->length);
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *new_offset = offset;
+ return 0;
+}
+
+int sys_read(int fd, void *data, size_t length, ssize_t *bytes_read) {
+ cacheFileTable();
+ auto lane = fileTable[fd];
+ HelAction actions[5];
+
+ managarm::fs::CntRequest<MemoryAllocator> req(getAllocator());
+ req.set_req_type(managarm::fs::CntReqType::READ);
+ req.set_size(length);
+
+ if(!globalQueue.valid())
+ globalQueue.initialize();
+
+ frg::string<MemoryAllocator> ser(getAllocator());
+ req.SerializeToString(&ser);
+ actions[0].type = kHelActionOffer;
+ actions[0].flags = kHelItemAncillary;
+ actions[1].type = kHelActionSendFromBuffer;
+ actions[1].flags = kHelItemChain;
+ actions[1].buffer = ser.data();
+ actions[1].length = ser.size();
+ actions[2].type = kHelActionImbueCredentials;
+ actions[2].handle = kHelThisThread;
+ actions[2].flags = kHelItemChain;
+ actions[3].type = kHelActionRecvInline;
+ actions[3].flags = kHelItemChain;
+ actions[4].type = kHelActionRecvToBuffer;
+ actions[4].flags = 0;
+ actions[4].buffer = data;
+ actions[4].length = length;
+ HEL_CHECK(helSubmitAsync(lane, actions, 5, globalQueue->getHandle(), 0, 0));
+
+ auto element = globalQueue->dequeueSingle();
+ auto offer = parseHandle(element);
+ auto send_req = parseSimple(element);
+ auto imbue_creds = parseSimple(element);
+ auto recv_resp = parseInline(element);
+ auto recv_data = parseLength(element);
+ HEL_CHECK(offer->error);
+ HEL_CHECK(send_req->error);
+ HEL_CHECK(imbue_creds->error);
+ HEL_CHECK(recv_resp->error);
+ HEL_CHECK(recv_data->error);
+
+ managarm::fs::SvrResponse<MemoryAllocator> resp(getAllocator());
+ resp.ParseFromArray(recv_resp->data, recv_resp->length);
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *bytes_read = recv_data->length;
+ return 0;
+}
+
+int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window) {
+ cacheFileTable();
+ HelAction actions[3];
+
+ managarm::posix::VmMapRequest<MemoryAllocator> req(getAllocator());
+ req.set_address_hint(reinterpret_cast<uintptr_t>(hint));
+ req.set_size(size);
+ req.set_mode(prot);
+ req.set_flags(flags);
+ req.set_fd(fd);
+ req.set_rel_offset(offset);
+
+ if(!globalQueue.valid())
+ globalQueue.initialize();
+
+ frg::string<MemoryAllocator> ser(getAllocator());
+ req.SerializeToString(&ser);
+ actions[0].type = kHelActionOffer;
+ actions[0].flags = kHelItemAncillary;
+ actions[1].type = kHelActionSendFromBuffer;
+ actions[1].flags = kHelItemChain;
+ actions[1].buffer = ser.data();
+ actions[1].length = ser.size();
+ actions[2].type = kHelActionRecvInline;
+ actions[2].flags = 0;
+ HEL_CHECK(helSubmitAsync(posixLane, actions, 3,
+ globalQueue->getHandle(), 0, 0));
+
+ auto element = globalQueue->dequeueSingle();
+ auto offer = parseHandle(element);
+ auto send_req = parseSimple(element);
+ auto recv_resp = parseInline(element);
+
+ HEL_CHECK(offer->error);
+ HEL_CHECK(send_req->error);
+ HEL_CHECK(recv_resp->error);
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getAllocator());
+ resp.ParseFromArray(recv_resp->data, recv_resp->length);
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ *window = reinterpret_cast<void *>(resp.offset());
+ return 0;
+}
+
+int sys_close(int fd) {
+ cacheFileTable();
+ HelAction actions[3];
+
+ managarm::posix::CloseRequest<MemoryAllocator> req(getAllocator());
+ req.set_fd(fd);
+
+ if(!globalQueue.valid())
+ globalQueue.initialize();
+
+ frg::string<MemoryAllocator> ser(getAllocator());
+ req.SerializeToString(&ser);
+ actions[0].type = kHelActionOffer;
+ actions[0].flags = kHelItemAncillary;
+ actions[1].type = kHelActionSendFromBuffer;
+ actions[1].flags = kHelItemChain;
+ actions[1].buffer = ser.data();
+ actions[1].length = ser.size();
+ actions[2].type = kHelActionRecvInline;
+ actions[2].flags = 0;
+ HEL_CHECK(helSubmitAsync(posixLane, actions, 3, globalQueue->getHandle(), 0, 0));
+
+ auto element = globalQueue->dequeueSingle();
+ auto offer = parseHandle(element);
+ auto send_req = parseSimple(element);
+ auto recv_resp = parseInline(element);
+ HEL_CHECK(offer->error);
+ HEL_CHECK(send_req->error);
+ HEL_CHECK(recv_resp->error);
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getAllocator());
+ resp.ParseFromArray(recv_resp->data, recv_resp->length);
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+}
+
+int sys_futex_tid() {
+ HelWord tid = 0;
+ HEL_CHECK(helSyscall0_1(kHelCallSuper + posix::superGetTid,
+ &tid));
+
+ return tid;
+}
+
+int sys_futex_wait(int *pointer, int expected, const struct timespec *time) {
+ // This implementation is inherently signal-safe.
+ if(time) {
+ if(helFutexWait(pointer, expected, time->tv_nsec + time->tv_sec * 1000000000))
+ return -1;
+ return 0;
+ }
+ if(helFutexWait(pointer, expected, -1))
+ return -1;
+ return 0;
+}
+
+int sys_futex_wake(int *pointer) {
+ // This implementation is inherently signal-safe.
+ if(helFutexWake(pointer))
+ return -1;
+ return 0;
+}
+
+
+} // namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/x86_64/crt-src/Scrt1.S b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/Scrt1.S
new file mode 100644
index 0000000..d0e8213
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/Scrt1.S
@@ -0,0 +1,8 @@
+.section .text
+.global _start
+_start:
+ mov %rsp, %rdi
+ lea main(%rip), %rsi
+ call __mlibc_entry
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crt0.S b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crt0.S
new file mode 100644
index 0000000..6afb421
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crt0.S
@@ -0,0 +1,10 @@
+
+.section .text
+.global _start
+_start:
+ mov %rsp, %rdi
+ mov $main, %rsi
+ call __mlibc_entry
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crti.S b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crti.S
new file mode 100644
index 0000000..1ca20f2
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crti.S
@@ -0,0 +1,15 @@
+ .ident "x86_64-managarm-mlibc crti"
+
+ .section .init
+ .globl _init
+ .type _init,@function
+_init:
+ push %rax
+
+ .section .fini
+ .globl _fini
+ .type _fini,@function
+_fini:
+ push %rax
+
+.section .note.GNU-stack,"",%progbits
diff --git a/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crtn.S b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crtn.S
new file mode 100644
index 0000000..fdb309e
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/x86_64/crt-src/crtn.S
@@ -0,0 +1,11 @@
+.ident "x86_64-managarm-mlibc crtn"
+
+.section .init
+ pop %rax
+ ret
+
+.section .fini
+ pop %rax
+ ret
+
+.section .note.GNU-stack,"",%progbits
diff --git a/lib/mlibc/sysdeps/managarm/x86_64/signals.S b/lib/mlibc/sysdeps/managarm/x86_64/signals.S
new file mode 100644
index 0000000..33ac4a3
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/x86_64/signals.S
@@ -0,0 +1,10 @@
+
+.section .text
+.global __mlibc_signal_restore
+__mlibc_signal_restore:
+ mov $0x80000006, %rdi
+ syscall
+ ud2
+
+.section .note.GNU-stack,"",%progbits
+
diff --git a/lib/mlibc/sysdeps/managarm/x86_64/thread.cpp b/lib/mlibc/sysdeps/managarm/x86_64/thread.cpp
new file mode 100644
index 0000000..88f1bf3
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/x86_64/thread.cpp
@@ -0,0 +1,56 @@
+#include <mlibc/thread-entry.hpp>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/tcb.hpp>
+#include <bits/ensure.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stddef.h>
+
+extern "C" void __mlibc_enter_thread(void *entry, void *user_arg, Tcb *tcb) {
+ // Wait until our parent sets up the TID.
+ while(!__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED))
+ mlibc::sys_futex_wait(&tcb->tid, 0, nullptr);
+
+ if(mlibc::sys_tcb_set(tcb))
+ __ensure(!"sys_tcb_set() failed");
+
+ tcb->invokeThreadFunc(entry, user_arg);
+
+ auto self = reinterpret_cast<Tcb *>(tcb);
+
+ __atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE);
+ mlibc::sys_futex_wake(&self->didExit);
+
+ mlibc::sys_thread_exit();
+}
+
+namespace mlibc {
+
+static constexpr size_t default_stacksize = 0x200000;
+
+int sys_prepare_stack(void **stack, void *entry, void *user_arg, void *tcb, size_t *stack_size, size_t *guard_size, void **stack_base) {
+ if (!*stack_size)
+ *stack_size = default_stacksize;
+ *guard_size = 0;
+
+ if (*stack) {
+ *stack_base = *stack;
+ } else {
+ *stack_base = mmap(nullptr, *stack_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if(*stack_base == MAP_FAILED) {
+ return errno;
+ }
+ }
+
+ uintptr_t *sp = reinterpret_cast<uintptr_t *>(reinterpret_cast<uintptr_t>(*stack_base) + *stack_size);
+
+ *--sp = reinterpret_cast<uintptr_t>(tcb);
+ *--sp = reinterpret_cast<uintptr_t>(user_arg);
+ *--sp = reinterpret_cast<uintptr_t>(entry);
+ *stack = reinterpret_cast<void*>(sp);
+ return 0;
+}
+
+} //namespace mlibc
diff --git a/lib/mlibc/sysdeps/managarm/x86_64/thread_entry.S b/lib/mlibc/sysdeps/managarm/x86_64/thread_entry.S
new file mode 100644
index 0000000..51e703b
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/x86_64/thread_entry.S
@@ -0,0 +1,11 @@
+
+.section .text
+.global __mlibc_start_thread
+__mlibc_start_thread:
+ pop %rdi
+ pop %rsi
+ pop %rdx
+ call __mlibc_enter_thread
+
+.section .note.GNU-stack,"",%progbits
+