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 AccessPermission, EventFlags, FileDescriptor, IoCtl, OpenOption, PollFd, dup_object,
25 get_object, 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!(
94 "__sys_alloc called with size {:#x}, align {:#x} is an invalid layout!",
95 size, align
96 );
97 return core::ptr::null_mut();
98 }
99 let layout = layout_res.unwrap();
100 let ptr = unsafe { ALLOCATOR.alloc(layout) };
101
102 trace!(
103 "__sys_alloc: allocate memory at {:p} (size {:#x}, align {:#x})",
104 ptr, size, align
105 );
106
107 ptr
108}
109
110#[cfg(all(target_os = "none", not(feature = "common-os")))]
111#[hermit_macro::system]
112#[unsafe(no_mangle)]
113pub extern "C" fn sys_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
114 let layout_res = Layout::from_size_align(size, align);
115 if layout_res.is_err() || size == 0 {
116 warn!(
117 "__sys_alloc_zeroed called with size {:#x}, align {:#x} is an invalid layout!",
118 size, align
119 );
120 return core::ptr::null_mut();
121 }
122 let layout = layout_res.unwrap();
123 let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) };
124
125 trace!(
126 "__sys_alloc_zeroed: allocate memory at {:p} (size {:#x}, align {:#x})",
127 ptr, size, align
128 );
129
130 ptr
131}
132
133#[cfg(all(target_os = "none", not(feature = "common-os")))]
134#[hermit_macro::system]
135#[unsafe(no_mangle)]
136pub extern "C" fn sys_malloc(size: usize, align: usize) -> *mut u8 {
137 let layout_res = Layout::from_size_align(size, align);
138 if layout_res.is_err() || size == 0 {
139 warn!(
140 "__sys_malloc called with size {:#x}, align {:#x} is an invalid layout!",
141 size, align
142 );
143 return core::ptr::null_mut();
144 }
145 let layout = layout_res.unwrap();
146 let ptr = unsafe { ALLOCATOR.alloc(layout) };
147
148 trace!(
149 "__sys_malloc: allocate memory at {:p} (size {:#x}, align {:#x})",
150 ptr, size, align
151 );
152
153 ptr
154}
155
156#[cfg(all(target_os = "none", not(feature = "common-os")))]
176#[hermit_macro::system]
177#[unsafe(no_mangle)]
178pub unsafe extern "C" fn sys_realloc(
179 ptr: *mut u8,
180 size: usize,
181 align: usize,
182 new_size: usize,
183) -> *mut u8 {
184 unsafe {
185 let layout_res = Layout::from_size_align(size, align);
186 if layout_res.is_err() || size == 0 || new_size == 0 {
187 warn!(
188 "__sys_realloc called with ptr {:p}, size {:#x}, align {:#x}, new_size {:#x} is an invalid layout!",
189 ptr, size, align, new_size
190 );
191 return core::ptr::null_mut();
192 }
193 let layout = layout_res.unwrap();
194 let new_ptr = ALLOCATOR.realloc(ptr, layout, new_size);
195
196 if new_ptr.is_null() {
197 debug!(
198 "__sys_realloc failed to resize ptr {:p} with size {:#x}, align {:#x}, new_size {:#x} !",
199 ptr, size, align, new_size
200 );
201 } else {
202 trace!(
203 "__sys_realloc: resized memory at {:p}, new address {:p}",
204 ptr, new_ptr
205 );
206 }
207 new_ptr
208 }
209}
210
211#[cfg(all(target_os = "none", not(feature = "common-os")))]
222#[hermit_macro::system]
223#[unsafe(no_mangle)]
224pub unsafe extern "C" fn sys_dealloc(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!(
229 "__sys_dealloc called with size {:#x}, align {:#x} is an invalid layout!",
230 size, align
231 );
232 debug_assert!(layout_res.is_err(), "__sys_dealloc error: Invalid layout");
233 debug_assert_ne!(size, 0, "__sys_dealloc error: size cannot be 0");
234 } else {
235 trace!(
236 "sys_free: deallocate memory at {:p} (size {:#x})",
237 ptr, size
238 );
239 }
240 let layout = layout_res.unwrap();
241 ALLOCATOR.dealloc(ptr, layout);
242 }
243}
244
245#[cfg(all(target_os = "none", not(feature = "common-os")))]
246#[hermit_macro::system]
247#[unsafe(no_mangle)]
248pub unsafe extern "C" fn sys_free(ptr: *mut u8, size: usize, align: usize) {
249 unsafe {
250 let layout_res = Layout::from_size_align(size, align);
251 if layout_res.is_err() || size == 0 {
252 warn!(
253 "__sys_free called with size {:#x}, align {:#x} is an invalid layout!",
254 size, align
255 );
256 debug_assert!(layout_res.is_err(), "__sys_free error: Invalid layout");
257 debug_assert_ne!(size, 0, "__sys_free error: size cannot be 0");
258 } else {
259 trace!(
260 "sys_free: deallocate memory at {:p} (size {:#x})",
261 ptr, size
262 );
263 }
264 let layout = layout_res.unwrap();
265 ALLOCATOR.dealloc(ptr, layout);
266 }
267}
268
269pub(crate) fn get_application_parameters() -> (i32, *const *const u8, *const *const u8) {
270 SYS.get_application_parameters()
271}
272
273pub(crate) fn shutdown(arg: i32) -> ! {
274 crate::arch::kernel::print_statistics();
276
277 SYS.shutdown(arg)
278}
279
280#[hermit_macro::system]
281#[unsafe(no_mangle)]
282pub unsafe extern "C" fn sys_unlink(name: *const c_char) -> i32 {
283 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
284
285 fs::unlink(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
286}
287
288#[hermit_macro::system]
289#[unsafe(no_mangle)]
290pub unsafe extern "C" fn sys_mkdir(name: *const c_char, mode: u32) -> i32 {
291 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
292 let Some(mode) = AccessPermission::from_bits(mode) else {
293 return -crate::errno::EINVAL;
294 };
295
296 crate::fs::create_dir(name, mode)
297 .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
298}
299
300#[hermit_macro::system]
301#[unsafe(no_mangle)]
302pub unsafe extern "C" fn sys_rmdir(name: *const c_char) -> i32 {
303 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
304
305 crate::fs::remove_dir(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
306}
307
308#[hermit_macro::system]
309#[unsafe(no_mangle)]
310pub unsafe extern "C" fn sys_stat(name: *const c_char, stat: *mut FileAttr) -> i32 {
311 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
312
313 match fs::read_stat(name) {
314 Ok(attr) => unsafe {
315 *stat = attr;
316 0
317 },
318 Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(),
319 }
320}
321
322#[hermit_macro::system]
323#[unsafe(no_mangle)]
324pub unsafe extern "C" fn sys_lstat(name: *const c_char, stat: *mut FileAttr) -> i32 {
325 let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
326
327 match fs::read_lstat(name) {
328 Ok(attr) => unsafe {
329 *stat = attr;
330 0
331 },
332 Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(),
333 }
334}
335
336#[hermit_macro::system]
337#[unsafe(no_mangle)]
338pub unsafe extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 {
339 if stat.is_null() {
340 return -crate::errno::EINVAL;
341 }
342
343 crate::fd::fstat(fd).map_or_else(
344 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
345 |v| unsafe {
346 *stat = v;
347 0
348 },
349 )
350}
351
352#[hermit_macro::system]
353#[unsafe(no_mangle)]
354pub unsafe extern "C" fn sys_opendir(name: *const c_char) -> FileDescriptor {
355 if let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() {
356 crate::fs::opendir(name).unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
357 } else {
358 -crate::errno::EINVAL
359 }
360}
361
362#[hermit_macro::system]
363#[unsafe(no_mangle)]
364pub unsafe extern "C" fn sys_open(name: *const c_char, flags: i32, mode: u32) -> FileDescriptor {
365 let Some(flags) = OpenOption::from_bits(flags) else {
366 return -crate::errno::EINVAL;
367 };
368 let Some(mode) = AccessPermission::from_bits(mode) else {
369 return -crate::errno::EINVAL;
370 };
371
372 if let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() {
373 crate::fs::open(name, flags, mode)
374 .unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
375 } else {
376 -crate::errno::EINVAL
377 }
378}
379
380#[hermit_macro::system]
381#[unsafe(no_mangle)]
382pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 {
383 let obj = remove_object(fd);
384 obj.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0)
385}
386
387#[hermit_macro::system]
388#[unsafe(no_mangle)]
389pub unsafe extern "C" fn sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize {
390 let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) };
391 crate::fd::read(fd, slice).map_or_else(
392 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
393 |v| v.try_into().unwrap(),
394 )
395}
396
397#[hermit_macro::system]
413#[unsafe(no_mangle)]
414pub unsafe extern "C" fn sys_readv(fd: i32, iov: *const iovec, iovcnt: usize) -> isize {
415 if !(0..=IOV_MAX).contains(&iovcnt) {
416 return (-crate::errno::EINVAL).try_into().unwrap();
417 }
418
419 let mut read_bytes: isize = 0;
420 let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
421
422 for iovec_buf in iovec_buffers {
423 let buf = unsafe { core::slice::from_raw_parts_mut(iovec_buf.iov_base, iovec_buf.iov_len) };
424
425 let len = crate::fd::read(fd, buf).map_or_else(
426 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
427 |v| v.try_into().unwrap(),
428 );
429
430 if len < 0 {
431 return len;
432 }
433
434 read_bytes += len;
435
436 if len < iovec_buf.iov_len.try_into().unwrap() {
437 return read_bytes;
438 }
439 }
440
441 read_bytes
442}
443
444unsafe fn write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
445 let slice = unsafe { core::slice::from_raw_parts(buf, len) };
446 crate::fd::write(fd, slice).map_or_else(
447 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
448 |v| v.try_into().unwrap(),
449 )
450}
451
452#[hermit_macro::system]
453#[unsafe(no_mangle)]
454pub unsafe extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize {
455 unsafe { write(fd, buf, len) }
456}
457
458#[hermit_macro::system]
474#[unsafe(no_mangle)]
475pub unsafe extern "C" fn sys_writev(fd: FileDescriptor, iov: *const iovec, iovcnt: usize) -> isize {
476 if !(0..=IOV_MAX).contains(&iovcnt) {
477 return (-crate::errno::EINVAL).try_into().unwrap();
478 }
479
480 let mut written_bytes: isize = 0;
481 let iovec_buffers = unsafe { core::slice::from_raw_parts(iov, iovcnt) };
482
483 for iovec_buf in iovec_buffers {
484 let buf = unsafe { core::slice::from_raw_parts(iovec_buf.iov_base, iovec_buf.iov_len) };
485
486 let len = crate::fd::write(fd, buf).map_or_else(
487 |e| -num::ToPrimitive::to_isize(&e).unwrap(),
488 |v| v.try_into().unwrap(),
489 );
490
491 if len < 0 {
492 return len;
493 }
494
495 written_bytes += len;
496
497 if len < iovec_buf.iov_len.try_into().unwrap() {
498 return written_bytes;
499 }
500 }
501
502 written_bytes
503}
504
505#[hermit_macro::system]
506#[unsafe(no_mangle)]
507pub unsafe extern "C" fn sys_ioctl(
508 fd: FileDescriptor,
509 cmd: i32,
510 argp: *mut core::ffi::c_void,
511) -> i32 {
512 const FIONBIO: i32 = 0x8008_667eu32 as i32;
513
514 if cmd == FIONBIO {
515 let value = unsafe { *(argp as *const i32) };
516
517 let obj = get_object(fd);
518 obj.map_or_else(
519 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
520 |v| {
521 block_on((*v).ioctl(IoCtl::NonBlocking, value != 0), None)
522 .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
523 },
524 )
525 } else {
526 -crate::errno::EINVAL
527 }
528}
529
530#[hermit_macro::system]
532#[unsafe(no_mangle)]
533pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 {
534 const F_SETFD: i32 = 2;
535 const F_SETFL: i32 = 4;
536 const FD_CLOEXEC: i32 = 1;
537 const O_NONBLOCK: i32 = 0o4000;
538
539 if cmd == F_SETFD && arg == FD_CLOEXEC {
540 0
541 } else if cmd == F_SETFL && arg == O_NONBLOCK {
542 let obj = get_object(fd);
543 obj.map_or_else(
544 |e| -num::ToPrimitive::to_i32(&e).unwrap(),
545 |v| {
546 block_on((*v).ioctl(IoCtl::NonBlocking, true), None)
547 .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |()| 0)
548 },
549 )
550 } else {
551 -crate::errno::EINVAL
552 }
553}
554
555#[hermit_macro::system]
556#[unsafe(no_mangle)]
557pub extern "C" fn sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> isize {
558 crate::fd::lseek(fd, offset, num::FromPrimitive::from_i32(whence).unwrap())
559 .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |_| 0)
560}
561
562#[repr(C)]
563#[derive(Debug, Clone, Copy)]
564pub struct Dirent64 {
565 pub d_ino: u64,
567 pub d_off: i64,
569 pub d_reclen: u16,
571 pub d_type: u8,
573 pub d_name: PhantomData<c_char>,
575}
576
577#[hermit_macro::system]
578#[unsafe(no_mangle)]
579pub unsafe extern "C" fn sys_getdents64(
580 fd: FileDescriptor,
581 dirp: *mut Dirent64,
582 count: usize,
583) -> i64 {
584 if dirp.is_null() || count == 0 {
585 return (-crate::errno::EINVAL).into();
586 }
587
588 const ALIGN_DIRENT: usize = core::mem::align_of::<Dirent64>();
589 let mut dirp: *mut Dirent64 = dirp;
590 let mut offset: i64 = 0;
591 let obj = get_object(fd);
592 obj.map_or_else(
593 |_| (-crate::errno::EINVAL).into(),
594 |v| {
595 block_on((*v).readdir(), None).map_or_else(
596 |e| -num::ToPrimitive::to_i64(&e).unwrap(),
597 |v| {
598 for i in v.iter() {
599 let len = i.name.len();
600 let aligned_len = ((core::mem::size_of::<Dirent64>() + len + 1)
601 + (ALIGN_DIRENT - 1)) & (!(ALIGN_DIRENT - 1));
602 if offset as usize + aligned_len >= count {
603 return (-crate::errno::EINVAL).into();
604 }
605
606 let dir = unsafe { &mut *dirp };
607
608 dir.d_ino = 0;
609 dir.d_type = 0;
610 dir.d_reclen = aligned_len.try_into().unwrap();
611 offset += i64::try_from(aligned_len).unwrap();
612 dir.d_off = offset;
613
614 let s = ptr::from_mut(&mut dir.d_name).cast::<u8>();
616 unsafe {
617 core::ptr::copy_nonoverlapping(i.name.as_ptr(), s, len);
618 s.add(len).write_bytes(0, 1);
619 }
620
621 dirp = unsafe { dirp.byte_add(aligned_len) };
622 }
623
624 offset
625 },
626 )
627 },
628 )
629}
630
631#[hermit_macro::system]
632#[unsafe(no_mangle)]
633pub extern "C" fn sys_dup(fd: i32) -> i32 {
634 dup_object(fd).unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
635}
636
637#[hermit_macro::system]
638#[unsafe(no_mangle)]
639pub unsafe extern "C" fn sys_poll(fds: *mut PollFd, nfds: usize, timeout: i32) -> i32 {
640 let slice = unsafe { core::slice::from_raw_parts_mut(fds, nfds) };
641 let timeout = if timeout >= 0 {
642 Some(core::time::Duration::from_millis(
643 timeout.try_into().unwrap(),
644 ))
645 } else {
646 None
647 };
648
649 crate::fd::poll(slice, timeout).map_or_else(
650 |e| {
651 if e == io::Error::ETIME {
652 0
653 } else {
654 -num::ToPrimitive::to_i32(&e).unwrap()
655 }
656 },
657 |v| v.try_into().unwrap(),
658 )
659}
660
661#[hermit_macro::system]
662#[unsafe(no_mangle)]
663pub extern "C" fn sys_eventfd(initval: u64, flags: i16) -> i32 {
664 if let Some(flags) = EventFlags::from_bits(flags) {
665 crate::fd::eventfd(initval, flags)
666 .unwrap_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap())
667 } else {
668 -crate::errno::EINVAL
669 }
670}
671
672#[hermit_macro::system]
673#[unsafe(no_mangle)]
674pub extern "C" fn sys_image_start_addr() -> usize {
675 crate::mm::kernel_start_address().as_usize()
676}
677
678#[cfg(test)]
679mod tests {
680 use super::*;
681
682 #[cfg(target_os = "none")]
683 #[test_case]
684 fn test_get_application_parameters() {
685 crate::env::init();
686 let (argc, argv, _envp) = get_application_parameters();
687 assert_ne!(argc, 0);
688 assert_ne!(argv, ptr::null());
689 }
690}