1#![allow(clippy::result_unit_err)]
2
3#[cfg(all(target_os = "none", not(feature = "common-os")))]
4use core::alloc::{GlobalAlloc, Layout};
5use core::ffi::{CStr, c_char};
6use core::marker::PhantomData;
7
8use dirent_display::Dirent64Display;
9use hermit_sync::Lazy;
10
11pub use self::condvar::*;
12pub use self::entropy::*;
13pub use self::futex::*;
14pub use self::processor::*;
15#[cfg(feature = "newlib")]
16pub use self::recmutex::*;
17pub use self::semaphore::*;
18pub use self::spinlock::*;
19pub use self::system::*;
20pub use self::tasks::*;
21pub use self::timer::*;
22use crate::env;
23use crate::errno::Errno;
24use crate::executor::block_on;
25use crate::fd::{
26 self, AccessPermission, EventFlags, FileDescriptor, OpenOption, PollFd, dup_object,
27 dup_object2, get_object, isatty, remove_object,
28};
29use crate::fs::{self, FileAttr, SeekWhence};
30#[cfg(all(target_os = "none", not(feature = "common-os")))]
31use crate::mm::ALLOCATOR;
32use crate::syscalls::interfaces::SyscallInterface;
33
34mod condvar;
35mod entropy;
36mod futex;
37pub(crate) mod interfaces;
38#[cfg(feature = "mman")]
39mod mman;
40mod processor;
41#[cfg(feature = "newlib")]
42mod recmutex;
43mod semaphore;
44#[cfg(any(feature = "tcp", feature = "udp", feature = "vsock"))]
45pub mod socket;
46mod spinlock;
47mod system;
48#[cfg(feature = "common-os")]
49pub(crate) mod table;
50mod tasks;
51mod timer;
52
53pub(crate) static SYS: Lazy<&'static dyn SyscallInterface> = Lazy::new(|| {
54 if env::is_uhyve() {
55 &self::interfaces::Uhyve
56 } else {
57 &self::interfaces::Generic
58 }
59});
60
61#[repr(C)]
62#[derive(Debug, Clone, Copy)]
63struct iovec {
65 pub iov_base: *mut u8,
67 pub iov_len: usize,
69}
70
71const IOV_MAX: usize = 1024;
72
73pub(crate) fn init() {
74 Lazy::force(&SYS);
75
76 SYS.init();
78
79 init_entropy();
80}
81
82#[cfg(all(target_os = "none", not(feature = "common-os")))]
89#[hermit_macro::system]
90#[unsafe(no_mangle)]
91pub extern "C" fn sys_alloc(size: usize, align: usize) -> *mut u8 {
92 let layout_res = Layout::from_size_align(size, align);
93 if layout_res.is_err() || size == 0 {
94 warn!("__sys_alloc called with size {size:#x}, align {align:#x} is an invalid layout!");
95 return core::ptr::null_mut();
96 }
97 let layout = layout_res.unwrap();
98 let ptr = unsafe { ALLOCATOR.alloc(layout) };
99
100 trace!("__sys_alloc: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
101
102 ptr
103}
104
105#[cfg(all(target_os = "none", not(feature = "common-os")))]
106#[hermit_macro::system]
107#[unsafe(no_mangle)]
108pub extern "C" fn sys_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
109 let layout_res = Layout::from_size_align(size, align);
110 if layout_res.is_err() || size == 0 {
111 warn!(
112 "__sys_alloc_zeroed called with size {size:#x}, align {align:#x} is an invalid layout!"
113 );
114 return core::ptr::null_mut();
115 }
116 let layout = layout_res.unwrap();
117 let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) };
118
119 trace!("__sys_alloc_zeroed: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
120
121 ptr
122}
123
124#[cfg(all(target_os = "none", not(feature = "common-os")))]
125#[hermit_macro::system]
126#[unsafe(no_mangle)]
127pub extern "C" fn sys_malloc(size: usize, align: usize) -> *mut u8 {
128 let layout_res = Layout::from_size_align(size, align);
129 if layout_res.is_err() || size == 0 {
130 warn!("__sys_malloc called with size {size:#x}, align {align:#x} is an invalid layout!");
131 return core::ptr::null_mut();
132 }
133 let layout = layout_res.unwrap();
134 let ptr = unsafe { ALLOCATOR.alloc(layout) };
135
136 trace!("__sys_malloc: allocate memory at {ptr:p} (size {size:#x}, align {align:#x})");
137
138 ptr
139}
140
141#[cfg(all(target_os = "none", not(feature = "common-os")))]
161#[hermit_macro::system]
162#[unsafe(no_mangle)]
163pub unsafe extern "C" fn sys_realloc(
164 ptr: *mut u8,
165 size: usize,
166 align: usize,
167 new_size: usize,
168) -> *mut u8 {
169 unsafe {
170 let layout_res = Layout::from_size_align(size, align);
171 if layout_res.is_err() || size == 0 || new_size == 0 {
172 warn!(
173 "__sys_realloc called with ptr {ptr:p}, size {size:#x}, align {align:#x}, new_size {new_size:#x} is an invalid layout!"
174 );
175 return core::ptr::null_mut();
176 }
177 let layout = layout_res.unwrap();
178 let new_ptr = ALLOCATOR.realloc(ptr, layout, new_size);
179
180 if new_ptr.is_null() {
181 debug!(
182 "__sys_realloc failed to resize ptr {ptr:p} with size {size:#x}, align {align:#x}, new_size {new_size:#x} !"
183 );
184 } else {
185 trace!("__sys_realloc: resized memory at {ptr:p}, new address {new_ptr:p}");
186 }
187 new_ptr
188 }
189}
190
191#[cfg(all(target_os = "none", not(feature = "common-os")))]
202#[hermit_macro::system]
203#[unsafe(no_mangle)]
204pub unsafe extern "C" fn sys_dealloc(ptr: *mut u8, size: usize, align: usize) {
205 unsafe {
206 let layout_res = Layout::from_size_align(size, align);
207 if layout_res.is_err() || size == 0 {
208 warn!(
209 "__sys_dealloc called with size {size:#x}, align {align:#x} is an invalid layout!"
210 );
211 debug_assert!(layout_res.is_err(), "__sys_dealloc error: Invalid layout");
212 debug_assert_ne!(size, 0, "__sys_dealloc error: size cannot be 0");
213 } else {
214 trace!("sys_free: deallocate memory at {ptr:p} (size {size:#x})");
215 }
216 let layout = layout_res.unwrap();
217 ALLOCATOR.dealloc(ptr, layout);
218 }
219}
220
221#[cfg(all(target_os = "none", not(feature = "common-os")))]
222#[hermit_macro::system]
223#[unsafe(no_mangle)]
224pub unsafe extern "C" fn sys_free(ptr: *mut u8, size: usize, align: usize) {
225 unsafe {
226 let layout_res = Layout::from_size_align(size, align);
227 if layout_res.is_err() || size == 0 {
228 warn!("__sys_free called with size {size:#x}, align {align:#x} is an invalid layout!");
229 debug_assert!(layout_res.is_err(), "__sys_free error: Invalid layout");
230 debug_assert_ne!(size, 0, "__sys_free error: size cannot be 0");
231 } else {
232 trace!("sys_free: deallocate memory at {ptr:p} (size {size:#x})");
233 }
234 let layout = layout_res.unwrap();
235 ALLOCATOR.dealloc(ptr, layout);
236 }
237}
238
239pub(crate) fn get_application_parameters() -> (i32, *const *const u8, *const *const u8) {
240 SYS.get_application_parameters()
241}
242
243pub(crate) fn shutdown(arg: i32) -> ! {
244 crate::arch::kernel::print_statistics();
246
247 SYS.shutdown(arg)
248}
249
250#[hermit_macro::system(errno)]
251#[unsafe(no_mangle)]
252pub unsafe extern "C" fn sys_unlink(name: *const c_char) -> i32 {
253 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
254
255 fs::unlink(name).map_or_else(|e| -i32::from(e), |()| 0)
256}
257
258#[hermit_macro::system(errno)]
259#[unsafe(no_mangle)]
260pub unsafe extern "C" fn sys_mkdir(name: *const c_char, mode: u32) -> i32 {
261 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
262 let Some(mode) = AccessPermission::from_bits(mode) else {
263 return -i32::from(Errno::Inval);
264 };
265
266 crate::fs::create_dir(name, mode).map_or_else(|e| -i32::from(e), |()| 0)
267}
268
269#[hermit_macro::system(errno)]
270#[unsafe(no_mangle)]
271pub unsafe extern "C" fn sys_rmdir(name: *const c_char) -> i32 {
272 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
273
274 crate::fs::remove_dir(name).map_or_else(|e| -i32::from(e), |()| 0)
275}
276
277#[hermit_macro::system(errno)]
278#[unsafe(no_mangle)]
279pub unsafe extern "C" fn sys_stat(name: *const c_char, stat: *mut FileAttr) -> i32 {
280 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
281
282 match fs::read_stat(name) {
283 Ok(attr) => unsafe {
284 *stat = attr;
285 0
286 },
287 Err(e) => -i32::from(e),
288 }
289}
290
291#[hermit_macro::system(errno)]
292#[unsafe(no_mangle)]
293pub unsafe extern "C" fn sys_lstat(name: *const c_char, stat: *mut FileAttr) -> i32 {
294 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
295
296 match fs::read_lstat(name) {
297 Ok(attr) => unsafe {
298 *stat = attr;
299 0
300 },
301 Err(e) => -i32::from(e),
302 }
303}
304
305#[hermit_macro::system(errno)]
306#[unsafe(no_mangle)]
307pub unsafe extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 {
308 if stat.is_null() {
309 return -i32::from(Errno::Inval);
310 }
311
312 crate::fd::fstat(fd).map_or_else(
313 |e| -i32::from(e),
314 |v| unsafe {
315 *stat = v;
316 0
317 },
318 )
319}
320
321#[hermit_macro::system(errno)]
322#[unsafe(no_mangle)]
323pub unsafe extern "C" fn sys_opendir(name: *const c_char) -> FileDescriptor {
324 if let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() {
325 crate::fs::opendir(name).unwrap_or_else(|e| -i32::from(e))
326 } else {
327 -i32::from(Errno::Inval)
328 }
329}
330
331#[hermit_macro::system(errno)]
332#[unsafe(no_mangle)]
333pub unsafe extern "C" fn sys_open(name: *const c_char, flags: i32, mode: u32) -> FileDescriptor {
334 let Some(flags) = OpenOption::from_bits(flags) else {
335 return -i32::from(Errno::Inval);
336 };
337 let Some(mode) = AccessPermission::from_bits(mode) else {
338 return -i32::from(Errno::Inval);
339 };
340
341 if let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() {
342 crate::fs::open(name, flags, mode).unwrap_or_else(|e| -i32::from(e))
343 } else {
344 -i32::from(Errno::Inval)
345 }
346}
347
348#[hermit_macro::system(errno)]
349#[unsafe(no_mangle)]
350pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 {
351 let obj = remove_object(fd);
352 obj.map_or_else(|e| -i32::from(e), |_| 0)
353}
354
355#[hermit_macro::system(errno)]
356#[unsafe(no_mangle)]
357pub unsafe extern "C" fn sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize {
358 let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast(), len) };
359 crate::fd::read(fd, slice).map_or_else(
360 |e| isize::try_from(-i32::from(e)).unwrap(),
361 |v| v.try_into().unwrap(),
362 )
363}
364
365#[hermit_macro::system(errno)]
381#[unsafe(no_mangle)]
382pub unsafe extern "C" fn sys_readv(fd: i32, iov: *const iovec, iovcnt: usize) -> isize {
383 if !(0..=IOV_MAX).contains(&iovcnt) {
384 return (-i32::from(Errno::Inval)).try_into().unwrap();
385 }
386
387 let mut read_bytes: isize = 0;
388 let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
389
390 for iovec_buf in iovec_buffers {
391 let buf = unsafe {
392 core::slice::from_raw_parts_mut(iovec_buf.iov_base.cast(), iovec_buf.iov_len)
393 };
394
395 let len = crate::fd::read(fd, buf).map_or_else(
396 |e| isize::try_from(-i32::from(e)).unwrap(),
397 |v| v.try_into().unwrap(),
398 );
399
400 if len < 0 {
401 return len;
402 }
403
404 read_bytes += len;
405
406 if len < isize::try_from(iovec_buf.iov_len).unwrap() {
407 return read_bytes;
408 }
409 }
410
411 read_bytes
412}
413
414unsafe fn write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
415 let slice = unsafe { core::slice::from_raw_parts(buf, len) };
416 crate::fd::write(fd, slice).map_or_else(
417 |e| isize::try_from(-i32::from(e)).unwrap(),
418 |v| v.try_into().unwrap(),
419 )
420}
421
422#[hermit_macro::system(errno)]
423#[unsafe(no_mangle)]
424pub unsafe extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
425 unsafe { write(fd, buf, len) }
426}
427
428#[hermit_macro::system(errno)]
444#[unsafe(no_mangle)]
445pub unsafe extern "C" fn sys_writev(fd: FileDescriptor, iov: *const iovec, iovcnt: usize) -> isize {
446 if !(0..=IOV_MAX).contains(&iovcnt) {
447 return (-i32::from(Errno::Inval)).try_into().unwrap();
448 }
449
450 let mut written_bytes: isize = 0;
451 let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
452
453 for iovec_buf in iovec_buffers {
454 let buf = unsafe { core::slice::from_raw_parts(iovec_buf.iov_base, iovec_buf.iov_len) };
455
456 let len = crate::fd::write(fd, buf).map_or_else(
457 |e| isize::try_from(-i32::from(e)).unwrap(),
458 |v| v.try_into().unwrap(),
459 );
460
461 if len < 0 {
462 return len;
463 }
464
465 written_bytes += len;
466
467 if len < isize::try_from(iovec_buf.iov_len).unwrap() {
468 return written_bytes;
469 }
470 }
471
472 written_bytes
473}
474
475#[hermit_macro::system(errno)]
476#[unsafe(no_mangle)]
477pub unsafe extern "C" fn sys_ioctl(
478 fd: FileDescriptor,
479 cmd: i32,
480 argp: *mut core::ffi::c_void,
481) -> i32 {
482 const FIONBIO: i32 = 0x8008_667eu32 as i32;
483
484 if cmd == FIONBIO {
485 let value = unsafe { *(argp as *const i32) };
486 let status_flags = if value != 0 {
487 fd::StatusFlags::O_NONBLOCK
488 } else {
489 fd::StatusFlags::empty()
490 };
491
492 let obj = get_object(fd);
493 obj.map_or_else(
494 |e| -i32::from(e),
495 |v| {
496 block_on((*v).set_status_flags(status_flags), None)
497 .map_or_else(|e| -i32::from(e), |()| 0)
498 },
499 )
500 } else {
501 -i32::from(Errno::Inval)
502 }
503}
504
505#[hermit_macro::system(errno)]
507#[unsafe(no_mangle)]
508pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 {
509 const F_SETFD: i32 = 2;
510 const F_GETFL: i32 = 3;
511 const F_SETFL: i32 = 4;
512 const FD_CLOEXEC: i32 = 1;
513
514 if cmd == F_SETFD && arg == FD_CLOEXEC {
515 0
516 } else if cmd == F_GETFL {
517 let obj = get_object(fd);
518 obj.map_or_else(
519 |e| -i32::from(e),
520 |v| {
521 block_on((*v).status_flags(), None)
522 .map_or_else(|e| -i32::from(e), |status_flags| status_flags.bits())
523 },
524 )
525 } else if cmd == F_SETFL {
526 let obj = get_object(fd);
527 obj.map_or_else(
528 |e| -i32::from(e),
529 |v| {
530 block_on(
531 (*v).set_status_flags(fd::StatusFlags::from_bits_retain(arg)),
532 None,
533 )
534 .map_or_else(|e| -i32::from(e), |()| 0)
535 },
536 )
537 } else {
538 -i32::from(Errno::Inval)
539 }
540}
541
542#[hermit_macro::system(errno)]
543#[unsafe(no_mangle)]
544pub extern "C" fn sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> isize {
545 let whence = u8::try_from(whence).unwrap();
546 let whence = SeekWhence::try_from(whence).unwrap();
547 crate::fd::lseek(fd, offset, whence).unwrap_or_else(|e| isize::try_from(-i32::from(e)).unwrap())
548}
549
550#[repr(C)]
551pub struct Dirent64 {
552 pub d_ino: u64,
554 pub d_off: i64,
556 pub d_reclen: u16,
558 pub d_type: fs::FileType,
560 pub d_name: PhantomData<c_char>,
562}
563impl Dirent64 {
564 #[allow(dead_code)]
570 unsafe fn display<'a>(&'a self) -> Dirent64Display<'a> {
571 unsafe { Dirent64Display::new(self) }
572 }
573}
574
575mod dirent_display {
576 use core::ffi::{CStr, c_char};
577 use core::fmt;
578
579 use super::Dirent64;
580
581 pub(super) struct Dirent64Display<'a> {
583 dirent: &'a Dirent64,
584 }
585 impl<'a> Dirent64Display<'a> {
586 pub(super) unsafe fn new(dirent: &'a Dirent64) -> Self {
589 Self { dirent }
590 }
591 }
592
593 impl<'a> fmt::Debug for Dirent64Display<'a> {
594 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
595 let cstr = unsafe { CStr::from_ptr((&raw const self.dirent.d_name).cast::<c_char>()) };
596
597 f.debug_struct("Dirent64")
598 .field("d_ino", &self.dirent.d_ino)
599 .field("d_off", &self.dirent.d_off)
600 .field("d_reclen", &self.dirent.d_reclen)
601 .field("d_type", &self.dirent.d_type)
602 .field("d_name", &cstr)
603 .finish()
604 }
605 }
606}
607
608#[hermit_macro::system(errno)]
625#[unsafe(no_mangle)]
626pub unsafe extern "C" fn sys_getdents64(
627 fd: FileDescriptor,
628 dirp: *mut Dirent64,
629 count: usize,
630) -> i64 {
631 debug!("getdents for fd {fd:?} - count: {count}");
632 if dirp.is_null() || count == 0 {
633 return (-i32::from(Errno::Inval)).into();
634 }
635
636 let slice = unsafe { core::slice::from_raw_parts_mut(dirp.cast(), count) };
637
638 let obj = get_object(fd);
639 obj.map_or_else(
640 |_| (-i32::from(Errno::Inval)).into(),
641 |v| {
642 block_on((*v).getdents(slice), None)
643 .map_or_else(|e| (-i32::from(e)).into(), |cnt| cnt as i64)
644 },
645 )
646}
647
648#[hermit_macro::system(errno)]
649#[unsafe(no_mangle)]
650pub extern "C" fn sys_dup(fd: i32) -> i32 {
651 dup_object(fd).unwrap_or_else(|e| -i32::from(e))
652}
653
654#[hermit_macro::system(errno)]
655#[unsafe(no_mangle)]
656pub extern "C" fn sys_dup2(fd1: i32, fd2: i32) -> i32 {
657 dup_object2(fd1, fd2).unwrap_or_else(|e| -i32::from(e))
658}
659
660#[hermit_macro::system(errno)]
661#[unsafe(no_mangle)]
662pub extern "C" fn sys_isatty(fd: i32) -> i32 {
663 match isatty(fd) {
664 Err(e) => -i32::from(e),
665 Ok(v) => {
666 if v {
667 1
668 } else {
669 0
670 }
671 }
672 }
673}
674
675#[hermit_macro::system(errno)]
676#[unsafe(no_mangle)]
677pub unsafe extern "C" fn sys_poll(fds: *mut PollFd, nfds: usize, timeout: i32) -> i32 {
678 let slice = unsafe { core::slice::from_raw_parts_mut(fds, nfds) };
679 let timeout = if timeout >= 0 {
680 Some(core::time::Duration::from_millis(
681 timeout.try_into().unwrap(),
682 ))
683 } else {
684 None
685 };
686
687 crate::fd::poll(slice, timeout).map_or_else(
688 |e| {
689 if e == Errno::Time { 0 } else { -i32::from(e) }
690 },
691 |v| v.try_into().unwrap(),
692 )
693}
694
695#[hermit_macro::system(errno)]
696#[unsafe(no_mangle)]
697pub extern "C" fn sys_eventfd(initval: u64, flags: i16) -> i32 {
698 if let Some(flags) = EventFlags::from_bits(flags) {
699 crate::fd::eventfd(initval, flags).unwrap_or_else(|e| -i32::from(e))
700 } else {
701 -i32::from(Errno::Inval)
702 }
703}
704
705#[hermit_macro::system]
706#[unsafe(no_mangle)]
707pub extern "C" fn sys_image_start_addr() -> usize {
708 crate::mm::kernel_start_address().as_usize()
709}
710
711#[cfg(test)]
712mod tests {
713 use core::ptr;
714
715 use super::*;
716
717 #[cfg(target_os = "none")]
718 #[test_case]
719 fn test_get_application_parameters() {
720 crate::env::init();
721 let (argc, argv, _envp) = get_application_parameters();
722 assert_ne!(argc, 0);
723 assert_ne!(argv, ptr::null());
724 }
725}