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;
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)]
63/// Describes  a  region  of  memory, beginning at `iov_base` address and with the size of `iov_len` bytes.
64struct iovec {
65	/// Starting address
66	pub iov_base: *mut u8,
67	/// Size of the memory pointed to by iov_base.
68	pub iov_len: usize,
69}
70
71const IOV_MAX: usize = 1024;
72
73pub(crate) fn init() {
74	Lazy::force(&SYS);
75
76	// Perform interface-specific initialization steps.
77	SYS.init();
78
79	init_entropy();
80}
81
82/// Interface to allocate memory from system heap
83///
84/// # Errors
85/// Returning a null pointer indicates that either memory is exhausted or
86/// `size` and `align` do not meet this allocator's size or alignment constraints.
87///
88#[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/// Shrink or grow a block of memory to the given `new_size`. The block is described by the given
142/// ptr pointer and layout. If this returns a non-null pointer, then ownership of the memory block
143/// referenced by ptr has been transferred to this allocator. The memory may or may not have been
144/// deallocated, and should be considered unusable (unless of course it was transferred back to the
145/// caller again via the return value of this method). The new memory block is allocated with
146/// layout, but with the size updated to new_size.
147/// If this method returns null, then ownership of the memory block has not been transferred to this
148/// allocator, and the contents of the memory block are unaltered.
149///
150/// # Safety
151/// This function is unsafe because undefined behavior can result if the caller does not ensure all
152/// of the following:
153/// - `ptr` must be currently allocated via this allocator,
154/// - `size` and `align` must be the same layout that was used to allocate that block of memory.
155/// ToDO: verify if the same values for size and align always lead to the same layout
156///
157/// # Errors
158/// Returns null if the new layout does not meet the size and alignment constraints of the
159/// allocator, or if reallocation otherwise fails.
160#[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/// Interface to deallocate a memory region from the system heap
192///
193/// # Safety
194/// This function is unsafe because undefined behavior can result if the caller does not ensure all of the following:
195/// - ptr must denote a block of memory currently allocated via this allocator,
196/// - `size` and `align` must be the same values that were used to allocate that block of memory
197/// ToDO: verify if the same values for size and align always lead to the same layout
198///
199/// # Errors
200/// May panic if debug assertions are enabled and invalid parameters `size` or `align` where passed.
201#[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	// print some performance statistics
245	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/// `read()` attempts to read `nbyte` of data to the object referenced by the
366/// descriptor `fd` from a buffer. `read()` performs the same
367/// action, but scatters the input data from the `iovcnt` buffers specified by the
368/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
369///
370/// ```
371/// struct iovec {
372///     char   *iov_base;  /* Base address. */
373///     size_t iov_len;    /* Length. */
374/// };
375/// ```
376///
377/// Each `iovec` entry specifies the base address and length of an area in memory from
378/// which data should be written.  `readv()` will always fill an completely
379/// before proceeding to the next.
380#[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/// `write()` attempts to write `nbyte` of data to the object referenced by the
429/// descriptor `fd` from a buffer. `writev()` performs the same
430/// action, but gathers the output data from the `iovcnt` buffers specified by the
431/// members of the iov array: `iov[0], iov[1], ..., iov[iovcnt-1]`.
432///
433/// ```
434/// struct iovec {
435///     char   *iov_base;  /* Base address. */
436///     size_t iov_len;    /* Length. */
437/// };
438/// ```
439///
440/// Each `iovec` entry specifies the base address and length of an area in memory from
441/// which data should be written.  `writev()` will always write a
442/// complete area before proceeding to the next.
443#[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/// manipulate file descriptor
506#[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	/// 64-bit inode number
553	pub d_ino: u64,
554	/// Field without meaning. Kept for BW compatibility.
555	pub d_off: i64,
556	/// Size of this dirent
557	pub d_reclen: u16,
558	/// File type
559	pub d_type: fs::FileType,
560	/// Filename (null-terminated)
561	pub d_name: PhantomData<c_char>,
562}
563impl Dirent64 {
564	/// Creates a [`Dirent64Display`] struct for debug printing.
565	///
566	/// # Safety
567	/// The bytes following the `d_name` must form a valid zero terminated `CStr`. Else we have an
568	/// out-of-bounds read.
569	#[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	/// Helperstruct for unsafe formatting of [`Dirent64`]
582	pub(super) struct Dirent64Display<'a> {
583		dirent: &'a Dirent64,
584	}
585	impl<'a> Dirent64Display<'a> {
586		/// # Safety
587		/// The `d_name` ptr of `dirent` must be valid and zero-terminated.
588		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/// Read the entries of a directory.
609/// Similar as the Linux system-call, this reads up to `count` bytes and returns the number of
610/// bytes written. If the size was not sufficient to list all directory entries, subsequent calls
611/// to this fn return the next entries.
612///
613/// Parameters:
614///
615/// - `fd`: File Descriptor of the directory in question.
616/// -`dirp`: Memory for the kernel to store the filled `Dirent64` objects including the c-strings with the filenames to.
617/// - `count`: Size of the memory region described by `dirp` in bytes.
618///
619/// Return:
620///
621/// The number of bytes read into `dirp` on success. Zero indicates that no more entries remain and
622/// the directories readposition needs to be reset using `sys_lseek`.
623/// Negative numbers encode errors.
624#[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}