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};
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]
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| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
255}
256
257#[hermit_macro::system]
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)
266 .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
267}
268
269#[hermit_macro::system]
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| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
275}
276
277#[hermit_macro::system]
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) => -num::ToPrimitive::to_i32(&e).unwrap(),
288 }
289}
290
291#[hermit_macro::system]
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) => -num::ToPrimitive::to_i32(&e).unwrap(),
302 }
303}
304
305#[hermit_macro::system]
306#[unsafe(no_mangle)]
307pub unsafe extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 {
308 if stat.is_null() {
309 return -crate::errno::EINVAL;
310 }
311
312 crate::fd::fstat(fd).map_or_else(
313 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
314 |v| unsafe {
315 *stat = v;
316 0
317 },
318 )
319}
320
321#[hermit_macro::system]
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| -num::ToPrimitive::to_i32(&e).unwrap())
326 } else {
327 -crate::errno::EINVAL
328 }
329}
330
331#[hermit_macro::system]
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 -crate::errno::EINVAL;
336 };
337 let Some(mode) = AccessPermission::from_bits(mode) else {
338 return -crate::errno::EINVAL;
339 };
340
341 if let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() {
342 crate::fs::open(name, flags, mode)
343 .unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
344 } else {
345 -crate::errno::EINVAL
346 }
347}
348
349#[hermit_macro::system]
350#[unsafe(no_mangle)]
351pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 {
352 let obj = remove_object(fd);
353 obj.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0)
354}
355
356#[hermit_macro::system]
357#[unsafe(no_mangle)]
358pub unsafe extern "C" fn sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize {
359 let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast(), len) };
360 crate::fd::read(fd, slice).map_or_else(
361 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
362 |v| v.try_into().unwrap(),
363 )
364}
365
366#[hermit_macro::system]
382#[unsafe(no_mangle)]
383pub unsafe extern "C" fn sys_readv(fd: i32, iov: *const iovec, iovcnt: usize) -> isize {
384 if !(0..=IOV_MAX).contains(&iovcnt) {
385 return (-crate::errno::EINVAL).try_into().unwrap();
386 }
387
388 let mut read_bytes: isize = 0;
389 let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
390
391 for iovec_buf in iovec_buffers {
392 let buf = unsafe {
393 core::slice::from_raw_parts_mut(iovec_buf.iov_base.cast(), iovec_buf.iov_len)
394 };
395
396 let len = crate::fd::read(fd, buf).map_or_else(
397 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
398 |v| v.try_into().unwrap(),
399 );
400
401 if len < 0 {
402 return len;
403 }
404
405 read_bytes += len;
406
407 if len < isize::try_from(iovec_buf.iov_len).unwrap() {
408 return read_bytes;
409 }
410 }
411
412 read_bytes
413}
414
415unsafe fn write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
416 let slice = unsafe { core::slice::from_raw_parts(buf, len) };
417 crate::fd::write(fd, slice).map_or_else(
418 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
419 |v| v.try_into().unwrap(),
420 )
421}
422
423#[hermit_macro::system]
424#[unsafe(no_mangle)]
425pub unsafe extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
426 unsafe { write(fd, buf, len) }
427}
428
429#[hermit_macro::system]
445#[unsafe(no_mangle)]
446pub unsafe extern "C" fn sys_writev(fd: FileDescriptor, iov: *const iovec, iovcnt: usize) -> isize {
447 if !(0..=IOV_MAX).contains(&iovcnt) {
448 return (-crate::errno::EINVAL).try_into().unwrap();
449 }
450
451 let mut written_bytes: isize = 0;
452 let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
453
454 for iovec_buf in iovec_buffers {
455 let buf = unsafe { core::slice::from_raw_parts(iovec_buf.iov_base, iovec_buf.iov_len) };
456
457 let len = crate::fd::write(fd, buf).map_or_else(
458 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
459 |v| v.try_into().unwrap(),
460 );
461
462 if len < 0 {
463 return len;
464 }
465
466 written_bytes += len;
467
468 if len < isize::try_from(iovec_buf.iov_len).unwrap() {
469 return written_bytes;
470 }
471 }
472
473 written_bytes
474}
475
476#[hermit_macro::system]
477#[unsafe(no_mangle)]
478pub unsafe extern "C" fn sys_ioctl(
479 fd: FileDescriptor,
480 cmd: i32,
481 argp: *mut core::ffi::c_void,
482) -> i32 {
483 const FIONBIO: i32 = 0x8008_667eu32 as i32;
484
485 if cmd == FIONBIO {
486 let value = unsafe { *(argp as *const i32) };
487 let status_flags = if value != 0 {
488 fd::StatusFlags::O_NONBLOCK
489 } else {
490 fd::StatusFlags::empty()
491 };
492
493 let obj = get_object(fd);
494 obj.map_or_else(
495 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
496 |v| {
497 block_on((*v).set_status_flags(status_flags), None)
498 .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
499 },
500 )
501 } else {
502 -crate::errno::EINVAL
503 }
504}
505
506#[hermit_macro::system]
508#[unsafe(no_mangle)]
509pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 {
510 const F_SETFD: i32 = 2;
511 const F_GETFL: i32 = 3;
512 const F_SETFL: i32 = 4;
513 const FD_CLOEXEC: i32 = 1;
514
515 if cmd == F_SETFD && arg == FD_CLOEXEC {
516 0
517 } else if cmd == F_GETFL {
518 let obj = get_object(fd);
519 obj.map_or_else(
520 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
521 |v| {
522 block_on((*v).status_flags(), None).map_or_else(
523 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
524 |status_flags| status_flags.bits(),
525 )
526 },
527 )
528 } else if cmd == F_SETFL {
529 let obj = get_object(fd);
530 obj.map_or_else(
531 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
532 |v| {
533 block_on(
534 (*v).set_status_flags(fd::StatusFlags::from_bits_retain(arg)),
535 None,
536 )
537 .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
538 },
539 )
540 } else {
541 -crate::errno::EINVAL
542 }
543}
544
545#[hermit_macro::system]
546#[unsafe(no_mangle)]
547pub extern "C" fn sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> isize {
548 crate::fd::lseek(fd, offset, num::FromPrimitive::from_i32(whence).unwrap())
549 .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |_| 0)
550}
551
552#[repr(C)]
553#[derive(Debug, Clone, Copy)]
554pub struct Dirent64 {
555 pub d_ino: u64,
557 pub d_off: i64,
559 pub d_reclen: u16,
561 pub d_type: u8,
563 pub d_name: PhantomData<c_char>,
565}
566
567#[hermit_macro::system]
568#[unsafe(no_mangle)]
569pub unsafe extern "C" fn sys_getdents64(
570 fd: FileDescriptor,
571 dirp: *mut Dirent64,
572 count: usize,
573) -> i64 {
574 if dirp.is_null() || count == 0 {
575 return (-crate::errno::EINVAL).into();
576 }
577
578 const ALIGN_DIRENT: usize = core::mem::align_of::<Dirent64>();
579 let mut dirp: *mut Dirent64 = dirp;
580 let mut offset: i64 = 0;
581 let obj = get_object(fd);
582 obj.map_or_else(
583 |_| (-crate::errno::EINVAL).into(),
584 |v| {
585 block_on((*v).readdir(), None).map_or_else(
586 |e| -num::ToPrimitive::to_i64(&e).unwrap(),
587 |v| {
588 for i in v.iter() {
589 let len = i.name.len();
590 let aligned_len = ((core::mem::size_of::<Dirent64>() + len + 1)
591 + (ALIGN_DIRENT - 1)) & (!(ALIGN_DIRENT - 1));
592 if offset as usize + aligned_len >= count {
593 return (-crate::errno::EINVAL).into();
594 }
595
596 let dir = unsafe { &mut *dirp };
597
598 dir.d_ino = 0;
599 dir.d_type = 0;
600 dir.d_reclen = aligned_len.try_into().unwrap();
601 offset += i64::try_from(aligned_len).unwrap();
602 dir.d_off = offset;
603
604 let s = ptr::from_mut(&mut dir.d_name).cast::<u8>();
606 unsafe {
607 core::ptr::copy_nonoverlapping(i.name.as_ptr(), s, len);
608 s.add(len).write_bytes(0, 1);
609 }
610
611 dirp = unsafe { dirp.byte_add(aligned_len) };
612 }
613
614 offset
615 },
616 )
617 },
618 )
619}
620
621#[hermit_macro::system]
622#[unsafe(no_mangle)]
623pub extern "C" fn sys_dup(fd: i32) -> i32 {
624 dup_object(fd).unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
625}
626
627#[hermit_macro::system]
628#[unsafe(no_mangle)]
629pub extern "C" fn sys_dup2(fd1: i32, fd2: i32) -> i32 {
630 dup_object2(fd1, fd2).unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
631}
632
633#[hermit_macro::system]
634#[unsafe(no_mangle)]
635pub extern "C" fn sys_isatty(fd: i32) -> i32 {
636 match isatty(fd) {
637 Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(),
638 Ok(v) => {
639 if v {
640 1
641 } else {
642 0
643 }
644 }
645 }
646}
647
648#[hermit_macro::system]
649#[unsafe(no_mangle)]
650pub unsafe extern "C" fn sys_poll(fds: *mut PollFd, nfds: usize, timeout: i32) -> i32 {
651 let slice = unsafe { core::slice::from_raw_parts_mut(fds, nfds) };
652 let timeout = if timeout >= 0 {
653 Some(core::time::Duration::from_millis(
654 timeout.try_into().unwrap(),
655 ))
656 } else {
657 None
658 };
659
660 crate::fd::poll(slice, timeout).map_or_else(
661 |e| {
662 if e == io::Error::ETIME {
663 0
664 } else {
665 -num::ToPrimitive::to_i32(&e).unwrap()
666 }
667 },
668 |v| v.try_into().unwrap(),
669 )
670}
671
672#[hermit_macro::system]
673#[unsafe(no_mangle)]
674pub extern "C" fn sys_eventfd(initval: u64, flags: i16) -> i32 {
675 if let Some(flags) = EventFlags::from_bits(flags) {
676 crate::fd::eventfd(initval, flags)
677 .unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
678 } else {
679 -crate::errno::EINVAL
680 }
681}
682
683#[hermit_macro::system]
684#[unsafe(no_mangle)]
685pub extern "C" fn sys_image_start_addr() -> usize {
686 crate::mm::kernel_start_address().as_usize()
687}
688
689#[cfg(test)]
690mod tests {
691 use super::*;
692
693 #[cfg(target_os = "none")]
694 #[test_case]
695 fn test_get_application_parameters() {
696 crate::env::init();
697 let (argc, argv, _envp) = get_application_parameters();
698 assert_ne!(argc, 0);
699 assert_ne!(argv, ptr::null());
700 }
701}