hermit/syscalls/
mod.rs

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)]
62/// Describes  a  region  of  memory, beginning at `iov_base` address and with the size of `iov_len` bytes.
63struct iovec {
64	/// Starting address
65	pub iov_base: *mut u8,
66	/// Size of the memory pointed to by iov_base.
67	pub iov_len: usize,
68}
69
70const IOV_MAX: usize = 1024;
71
72pub(crate) fn init() {
73	Lazy::force(&SYS);
74
75	// Perform interface-specific initialization steps.
76	SYS.init();
77
78	init_entropy();
79}
80
81/// Interface to allocate memory from system heap
82///
83/// # Errors
84/// Returning a null pointer indicates that either memory is exhausted or
85/// `size` and `align` do not meet this allocator's size or alignment constraints.
86///
87#[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/// Shrink or grow a block of memory to the given `new_size`. The block is described by the given
141/// ptr pointer and layout. If this returns a non-null pointer, then ownership of the memory block
142/// referenced by ptr has been transferred to this allocator. The memory may or may not have been
143/// deallocated, and should be considered unusable (unless of course it was transferred back to the
144/// caller again via the return value of this method). The new memory block is allocated with
145/// layout, but with the size updated to new_size.
146/// If this method returns null, then ownership of the memory block has not been transferred to this
147/// allocator, and the contents of the memory block are unaltered.
148///
149/// # Safety
150/// This function is unsafe because undefined behavior can result if the caller does not ensure all
151/// of the following:
152/// - `ptr` must be currently allocated via this allocator,
153/// - `size` and `align` must be the same layout that was used to allocate that block of memory.
154/// ToDO: verify if the same values for size and align always lead to the same layout
155///
156/// # Errors
157/// Returns null if the new layout does not meet the size and alignment constraints of the
158/// allocator, or if reallocation otherwise fails.
159#[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/// Interface to deallocate a memory region from the system heap
191///
192/// # Safety
193/// This function is unsafe because undefined behavior can result if the caller does not ensure all of the following:
194/// - ptr must denote a block of memory currently allocated via this allocator,
195/// - `size` and `align` must be the same values that were used to allocate that block of memory
196/// ToDO: verify if the same values for size and align always lead to the same layout
197///
198/// # Errors
199/// May panic if debug assertions are enabled and invalid parameters `size` or `align` where passed.
200#[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	// print some performance statistics
244	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/// `read()` attempts to read `nbyte` of data to the object referenced by the
365/// descriptor `fd` from a buffer. `read()` performs the same
366/// action, but scatters the input data from the `iovcnt` buffers specified by the
367/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
368///
369/// ```
370/// struct iovec {
371///     char   *iov_base;  /* Base address. */
372///     size_t iov_len;    /* Length. */
373/// };
374/// ```
375///
376/// Each `iovec` entry specifies the base address and length of an area in memory from
377/// which data should be written.  `readv()` will always fill an completely
378/// before proceeding to the next.
379#[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/// `write()` attempts to write `nbyte` of data to the object referenced by the
428/// descriptor `fd` from a buffer. `writev()` performs the same
429/// action, but gathers the output data from the `iovcnt` buffers specified by the
430/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
431///
432/// ```
433/// struct iovec {
434///     char   *iov_base;  /* Base address. */
435///     size_t iov_len;    /* Length. */
436/// };
437/// ```
438///
439/// Each `iovec` entry specifies the base address and length of an area in memory from
440/// which data should be written.  `writev()` will always write a
441/// complete area before proceeding to the next.
442#[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/// manipulate file descriptor
505#[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	/// 64-bit inode number
553	pub d_ino: u64,
554	/// 64-bit offset to next structure
555	pub d_off: i64,
556	/// Size of this dirent
557	pub d_reclen: u16,
558	/// File type
559	pub d_type: u8,
560	/// Filename (null-terminated)
561	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						// copy null-terminated filename
602						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}