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