diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index 9e7ff620fea..4a0d598f5ac 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -42,3 +42,4 @@ pub(crate) mod windows_msvc; pub(crate) mod windows_uwp_gnu; pub(crate) mod windows_uwp_msvc; pub(crate) mod xtensa; +pub(crate) mod yggdrasil; diff --git a/compiler/rustc_target/src/spec/base/yggdrasil.rs b/compiler/rustc_target/src/spec/base/yggdrasil.rs new file mode 100644 index 00000000000..65c036b3212 --- /dev/null +++ b/compiler/rustc_target/src/spec/base/yggdrasil.rs @@ -0,0 +1,36 @@ +use crate::spec::{ + Cc, LinkOutputKind, LinkerFlavor, Lld, Os, RelocModel, StackProbeType, TargetOptions, + crt_objects, +}; + +pub(crate) fn opts() -> TargetOptions { + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-zmax-page-size=4096", "--dynamic-linker=/libexec/dyn-loader"], + ); + + TargetOptions { + os: Os::Yggdrasil, + + linker: Some("rust-lld".into()), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + pre_link_objects: crt_objects::new(&[ + (LinkOutputKind::DynamicNoPicExe, &["rust_entry0.o"]), + (LinkOutputKind::DynamicPicExe, &["rust_entry0.o"]), + (LinkOutputKind::StaticNoPicExe, &["rust_entry0.o"]), + (LinkOutputKind::StaticPicExe, &["rust_entry0.o"]), + ]), + executables: true, + dynamic_linking: true, + position_independent_executables: true, + relocation_model: RelocModel::Pic, + + pre_link_args, + stack_probes: StackProbeType::Inline, + max_atomic_width: Some(128), + eh_frame_header: false, + has_thread_local: true, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 57effe3a866..9f7a6e0cf8b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1801,6 +1801,10 @@ supported_targets! { ("x86_64-lynx-lynxos178", x86_64_lynx_lynxos178), ("x86_64-pc-cygwin", x86_64_pc_cygwin), + + ("aarch64-unknown-yggdrasil", aarch64_unknown_yggdrasil), + ("riscv64-unknown-yggdrasil", riscv64_unknown_yggdrasil), + ("x86_64-unknown-yggdrasil", x86_64_unknown_yggdrasil), } /// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]> @@ -1994,6 +1998,7 @@ crate::target_spec_enum! { Windows = "windows", Xous = "xous", Zkvm = "zkvm", + Yggdrasil = "yggdrasil", Unknown = "unknown", } other_variant = Other; diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_yggdrasil.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_yggdrasil.rs new file mode 100644 index 00000000000..369b2635e99 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_yggdrasil.rs @@ -0,0 +1,24 @@ +use crate::spec::{Arch, PanicStrategy, Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = crate::spec::base::yggdrasil::opts(); + + base.disable_redzone = true; + base.panic_strategy = PanicStrategy::Abort; + base.features = "+fp-armv8,+neon,+strict-align,+v8a".into(); + base.plt_by_default = true; + + Target { + llvm_target: "aarch64-unknown-none".into(), + pointer_width: 64, + arch: Arch::AArch64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + metadata: TargetMetadata { + description: Some("AArch64 yggdrasil".into()), + tier: Some(1), + host_tools: Some(true), + std: Some(true), + }, + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv64_unknown_yggdrasil.rs b/compiler/rustc_target/src/spec/targets/riscv64_unknown_yggdrasil.rs new file mode 100644 index 00000000000..854bcdfae26 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv64_unknown_yggdrasil.rs @@ -0,0 +1,32 @@ +use crate::spec::{Arch, CodeModel, PanicStrategy, Target, TargetMetadata, TargetOptions}; + +pub(crate) fn target() -> Target { + let base = crate::spec::base::yggdrasil::opts(); + + Target { + llvm_target: "riscv64".into(), + pointer_width: 64, + arch: Arch::RiscV64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + metadata: TargetMetadata { + description: Some("RISC-V 64-bit yggdrasil".into()), + tier: Some(1), + host_tools: Some(false), + std: Some(true), + }, + options: TargetOptions { + code_model: Some(CodeModel::Medium), + disable_redzone: true, + max_atomic_width: Some(64), + features: "+m,+a,+c,+zicsr,+zifencei".into(), + panic_strategy: PanicStrategy::Abort, + llvm_abiname: "lp64".into(), + plt_by_default: true, + + ..base // From android + // features: "+m,+a,+f,+d,+c,+b,+v,+zicsr,+zifencei".into(), + // supported_sanitizers: SanitizerSet::ADDRESS, + // supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_yggdrasil.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_yggdrasil.rs new file mode 100644 index 00000000000..fa3434a0cdb --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_yggdrasil.rs @@ -0,0 +1,25 @@ +use crate::spec::{Arch, PanicStrategy, Target, TargetMetadata}; + +pub(crate) fn target() -> Target { + let mut base = crate::spec::base::yggdrasil::opts(); + + base.disable_redzone = true; + base.panic_strategy = PanicStrategy::Abort; + base.plt_by_default = true; + base.features = "+sse,+cx16".into(); + + Target { + llvm_target: "x86_64-unknown-linux-gnu".into(), + pointer_width: 64, + arch: Arch::X86_64, + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + metadata: TargetMetadata { + description: Some("x86_64 yggdrasil".into()), + tier: Some(1), + host_tools: Some(true), + std: Some(true), + }, + options: base, + } +} diff --git a/library/Cargo.lock b/library/Cargo.lock index f6c14bc58a0..07fee0994b6 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -2,6 +2,31 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "abi-generator" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "thiserror", +] + +[[package]] +name = "abi-lib" +version = "0.1.0" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "abi-serde" +version = "0.1.0" +dependencies = [ + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -153,6 +178,22 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "libm" +version = "0.2.8" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "libyalloc" +version = "0.1.0" +dependencies = [ + "libc", + "rustc-std-workspace-core", + "yggdrasil-rt", +] + [[package]] name = "memchr" version = "2.7.6" @@ -213,6 +254,25 @@ dependencies = [ "unwind", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + [[package]] name = "proc_macro" version = "0.0.0" @@ -229,6 +289,15 @@ dependencies = [ "cc", ] +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + [[package]] name = "r-efi" version = "5.3.0" @@ -331,6 +400,7 @@ dependencies = [ "hashbrown", "hermit-abi", "libc", + "libyalloc", "miniz_oxide", "moto-rt", "object", @@ -347,6 +417,7 @@ dependencies = [ "wasi 0.11.1+wasi-snapshot-preview1", "wasi 0.14.4+wasi-0.2.4", "windows-targets 0.0.0", + "yggdrasil-rt", ] [[package]] @@ -358,6 +429,17 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sysroot" version = "0.0.0" @@ -378,6 +460,32 @@ dependencies = [ "std", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + [[package]] name = "unwind" version = "0.0.0" @@ -520,3 +628,30 @@ dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] + +[[package]] +name = "yggdrasil-abi" +version = "0.1.0" +dependencies = [ + "abi-generator", + "abi-lib", + "abi-serde", + "prettyplease", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "yggdrasil-rt" +version = "0.1.0" +dependencies = [ + "abi-generator", + "abi-lib", + "abi-serde", + "cc", + "libm", + "prettyplease", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "yggdrasil-abi", +] diff --git a/library/rtstartup/yggdrasil_rust_entry.rs b/library/rtstartup/yggdrasil_rust_entry.rs new file mode 100644 index 00000000000..6001811ffc8 --- /dev/null +++ b/library/rtstartup/yggdrasil_rust_entry.rs @@ -0,0 +1,38 @@ +#![feature(no_core, lang_items, auto_traits)] +#![allow(internal_features)] +#![crate_type = "rlib"] +#![no_core] +#![no_main] + +#[lang = "pointee_sized"] +pub trait PointeeSized {} +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} +#[lang = "sized"] +pub trait Sized: MetaSized {} + +#[lang = "sync"] +auto trait Sync {} +#[lang = "copy"] +trait Copy {} +#[lang = "freeze"] +auto trait Freeze {} + +impl ::Copy for usize {} + +// #[lang = "drop_in_place"] +// #[inline] +// #[allow(unconditional_recursion)] +// pub unsafe fn drop_in_place(to_drop: *mut T) { +// drop_in_place(to_drop); +// } + +#[unsafe(no_mangle)] +unsafe extern "C" fn _start(arg: usize) -> ! { + extern "C" { + fn __rust_start(arg: usize, main: usize) -> !; + fn main(argc: isize, argv: *const *const u8) -> i32; + } + + __rust_start(arg, main as *const () as usize) +} diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 5c9ae52d9e6..3c8ca9cbfd1 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -102,6 +102,10 @@ vex-sdk = { version = "0.27.0", features = [ 'rustc-dep-of-std', ], default-features = false } +[target.'cfg(target_os = "yggdrasil")'.dependencies] +yggdrasil-rt = { path = "../../../lib/runtime", features = ['rustc-dep-of-std'] } +libyalloc = { path = "../../../lib/libyalloc", features = ['rustc-dep-of-std'] } + [features] backtrace = [ 'addr2line/rustc-dep-of-std', diff --git a/library/std/build.rs b/library/std/build.rs index c0a6e30b380..679437fba77 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -57,6 +57,7 @@ fn main() { || target_os == "nuttx" || target_os == "cygwin" || target_os == "vexos" + || target_os == "yggdrasil" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 76374402be4..ee706078970 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -183,6 +183,8 @@ pub mod vita; pub mod vxworks; #[cfg(target_os = "xous")] pub mod xous; +#[cfg(target_os = "yggdrasil")] +pub mod yggdrasil; #[cfg(any( unix, @@ -194,5 +196,9 @@ pub mod xous; ))] pub mod fd; +#[stable(feature = "os_fd", since = "1.66.0")] +#[cfg(target_os = "yggdrasil")] +pub use yggdrasil::io::fd; + #[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] mod net; diff --git a/library/std/src/os/yggdrasil/fs.rs b/library/std/src/os/yggdrasil/fs.rs new file mode 100644 index 00000000000..77c22f6d8b5 --- /dev/null +++ b/library/std/src/os/yggdrasil/fs.rs @@ -0,0 +1,74 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::io::FileMode; +use yggdrasil_rt::sys as syscall; + +use crate::fs::{FileType, Metadata, OpenOptions}; +use crate::io; +use crate::path::Path; +use crate::sys::{AsInner, AsInnerMut}; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub trait MetadataExt { + fn mode_ext(&self) -> FileMode; + fn inode(&self) -> Option; + fn block_count(&self) -> u64; + fn block_size(&self) -> u64; +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub trait FileTypeExt { + fn is_block_device(&self) -> bool; + fn is_char_device(&self) -> bool; +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub trait OpenOptionsExt { + fn mode_ext(&mut self, mode: FileMode) -> &mut Self; +} + +impl MetadataExt for Metadata { + fn mode_ext(&self) -> FileMode { + self.as_inner().mode_ext() + } + + fn inode(&self) -> Option { + self.as_inner().inode() + } + + fn block_count(&self) -> u64 { + self.as_inner().block_count() + } + + fn block_size(&self) -> u64 { + self.as_inner().block_size() + } +} + +impl OpenOptionsExt for OpenOptions { + fn mode_ext(&mut self, mode: FileMode) -> &mut Self { + self.as_inner_mut().mode = mode; + self + } +} + +impl FileTypeExt for FileType { + #[inline] + fn is_block_device(&self) -> bool { + self.as_inner().is_block_device() + } + + #[inline] + fn is_char_device(&self) -> bool { + self.as_inner().is_char_device() + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn symlink, Q: AsRef>(target: P, path: Q) -> Result<(), io::Error> { + let target = target.as_ref().to_str().unwrap(); + let path = path.as_ref().to_str().unwrap(); + + unsafe { syscall::create_symlink(None, target, path) }?; + Ok(()) +} diff --git a/library/std/src/os/yggdrasil/io/device.rs b/library/std/src/os/yggdrasil/io/device.rs new file mode 100644 index 00000000000..ed59f93dab8 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/device.rs @@ -0,0 +1,18 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::io::device as rt; +use yggdrasil_rt::sys as syscall; + +use crate::io; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub type MountOptions<'a> = rt::MountOptions<'a>; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub type UnmountOptions = rt::UnmountOptions; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub unsafe fn mount_raw(options: &MountOptions<'_>) -> io::Result<()> { + syscall::mount(options)?; + Ok(()) +} diff --git a/library/std/src/os/yggdrasil/io/fd/mod.rs b/library/std/src/os/yggdrasil/io/fd/mod.rs new file mode 100644 index 00000000000..b8ebac91995 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/fd/mod.rs @@ -0,0 +1,12 @@ +mod owned; +mod raw; + +pub use owned::*; +pub use raw::*; + +use crate::io; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn clone_fd(src: RawFd) -> io::Result { + Ok(unsafe { yggdrasil_rt::sys::clone_fd(src, None) }?) +} diff --git a/library/std/src/os/yggdrasil/io/fd/owned.rs b/library/std/src/os/yggdrasil/io/fd/owned.rs new file mode 100644 index 00000000000..19c04b96631 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/fd/owned.rs @@ -0,0 +1,150 @@ +#![stable(feature = "io_safety", since = "1.63.0")] + +use super::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::marker::PhantomData; +use crate::mem::forget; +use crate::sys::IntoInner; + +macro_rules! stdio_impl_as_fd { + ($($ty:ty => $n:expr),*) => {$( + #[stable(feature = "io_safety", since = "1.63.0")] + impl AsFd for $ty { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + BorrowedFd { + fd: $n, + _pd: PhantomData + } + } + } + + // #[stable(feature = "io_safety", since = "1.21.0")] + // impl AsFd for &$ty { + // #[inline] + // fn as_raw_fd(&self) -> RawFd { + // $n + // } + // } + )*}; +} + +#[stable(feature = "io_safety", since = "1.63.0")] +#[repr(transparent)] +#[derive(Debug)] +pub struct OwnedFd { + fd: RawFd, +} + +#[stable(feature = "io_safety", since = "1.63.0")] +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct BorrowedFd<'fd> { + fd: RawFd, + _pd: PhantomData<&'fd OwnedFd>, +} + +#[stable(feature = "io_safety", since = "1.63.0")] +pub trait AsFd { + #[stable(feature = "io_safety", since = "1.63.0")] + fn as_fd(&self) -> BorrowedFd<'_>; +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for &T { + fn as_fd(&self) -> BorrowedFd<'_> { + T::as_fd(self) + } +} + +// Borrowed + +impl BorrowedFd<'_> { + #[rustc_const_stable(feature = "io_safety", since = "1.63.0")] + #[stable(feature = "io_safety", since = "1.63.0")] + pub const unsafe fn borrow_raw(fd: RawFd) -> Self { + // assert_ne!(fd, u32::MAX as RawFd); + Self { fd, _pd: PhantomData } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for BorrowedFd<'_> { + fn as_fd(&self) -> BorrowedFd<'_> { + *self + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsRawFd for BorrowedFd<'_> { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +// Owned + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for OwnedFd { + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl IntoRawFd for OwnedFd { + fn into_raw_fd(self) -> RawFd { + let fd = self.fd; + forget(self); + fd + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl FromRawFd for OwnedFd { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self { fd } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsRawFd for OwnedFd { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl Drop for OwnedFd { + fn drop(&mut self) { + unsafe { + yggdrasil_rt::sys::close(self.fd).ok(); + } + } +} + +// TODO: AsFd for File, From for OwnedFd, From for File, +#[stable(feature = "io_safety", since = "1.63.0")] +impl From for OwnedFd { + fn from(value: crate::fs::File) -> OwnedFd { + let inner = value.into_inner(); + Self { fd: inner.into_raw_fd() } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for crate::fs::File { + fn as_fd(&self) -> BorrowedFd<'_> { + BorrowedFd { fd: self.as_raw_fd(), _pd: PhantomData } + } +} + +stdio_impl_as_fd!( + crate::io::Stdin => RawFd::STDIN, + crate::io::Stdout => RawFd::STDOUT, + crate::io::Stderr => RawFd::STDERR +); +stdio_impl_as_fd!( + crate::io::StdinLock<'_> => RawFd::STDIN, + crate::io::StdoutLock<'_> => RawFd::STDOUT, + crate::io::StderrLock<'_> => RawFd::STDERR +); diff --git a/library/std/src/os/yggdrasil/io/fd/raw.rs b/library/std/src/os/yggdrasil/io/fd/raw.rs new file mode 100644 index 00000000000..d2f435d09f4 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/fd/raw.rs @@ -0,0 +1,98 @@ +#![stable(feature = "os_fd", since = "1.66.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = yggdrasil_rt::io::RawFd; + +use crate::sys::{AsInner, FromInner, IntoInner}; + +macro_rules! stdio_impl_as_raw_fd { + ($($ty:ty => $n:expr),*) => {$( + #[stable(feature = "asraw_stdio", since = "1.21.0")] + impl AsRawFd for $ty { + #[inline] + fn as_raw_fd(&self) -> RawFd { + $n + } + } + + #[stable(feature = "asraw_stdio", since = "1.21.0")] + impl AsRawFd for &$ty { + #[inline] + fn as_raw_fd(&self) -> RawFd { + $n + } + } + )*}; +} + +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawFd { + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_fd(&self) -> RawFd; +} + +#[stable(feature = "rust1", since = "1.0.0")] +pub trait FromRawFd { + #[stable(feature = "rust1", since = "1.0.0")] + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +pub trait IntoRawFd { + #[stable(feature = "rust1", since = "1.0.0")] + fn into_raw_fd(self) -> RawFd; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for RawFd { + fn as_raw_fd(&self) -> RawFd { + *self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromRawFd for RawFd { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + fd + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoRawFd for RawFd { + fn into_raw_fd(self) -> RawFd { + self + } +} + +stdio_impl_as_raw_fd!( + crate::io::Stdin => RawFd::STDIN, + crate::io::Stdout => RawFd::STDOUT, + crate::io::Stderr => RawFd::STDERR +); +stdio_impl_as_raw_fd!( + crate::io::StdinLock<'_> => RawFd::STDIN, + crate::io::StdoutLock<'_> => RawFd::STDOUT, + crate::io::StderrLock<'_> => RawFd::STDERR +); + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for crate::fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromRawFd for crate::fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let inner = crate::sys::fs::File::from_raw_fd(fd); + crate::fs::File::from_inner(inner) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoRawFd for crate::fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_raw_fd() + } +} diff --git a/library/std/src/os/yggdrasil/io/mapping.rs b/library/std/src/os/yggdrasil/io/mapping.rs new file mode 100644 index 00000000000..58261c2a656 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/mapping.rs @@ -0,0 +1,67 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use super::fd::RawFd; +use crate::os::fd::{AsRawFd, OwnedFd}; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub struct FileMapping<'a> { + data: &'a mut [u8], + fd: OwnedFd, +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl<'a> FileMapping<'a> { + pub fn new>(f: F, offset: u64, len: usize) -> crate::io::Result { + use yggdrasil_rt::mem::{MappingFlags, MappingSource}; + + let owned_fd = f.into(); + let raw_fd = owned_fd.as_raw_fd(); + let base = unsafe { + yggdrasil_rt::sys::map_memory( + None, + len, + MappingFlags::WRITE, + &MappingSource::File(raw_fd, offset), + ) + }?; + + #[allow(fuzzy_provenance_casts)] + let data = unsafe { crate::slice::from_raw_parts_mut(base as *mut u8, len) }; + + Ok(Self { data, fd: owned_fd }) + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl crate::ops::Deref for FileMapping<'_> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.data + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl crate::ops::DerefMut for FileMapping<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.data + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl Drop for FileMapping<'_> { + fn drop(&mut self) { + // Unmap the memory + let base = self.data.as_ptr() as usize; + let len = self.data.len(); + unsafe { + yggdrasil_rt::sys::unmap_memory(base, len).expect("Memory unmap failed"); + } + } +} + +impl AsRawFd for FileMapping<'_> { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} diff --git a/library/std/src/os/yggdrasil/io/mod.rs b/library/std/src/os/yggdrasil/io/mod.rs new file mode 100644 index 00000000000..7e3fc1fc3af --- /dev/null +++ b/library/std/src/os/yggdrasil/io/mod.rs @@ -0,0 +1,25 @@ +#![allow(dead_code)] + +pub mod fd; + +pub mod device; +pub mod mapping; +// pub mod message_channel; +pub mod net; +//pub mod pid; +pub mod pipe; +pub mod poll; +pub mod shared_memory; +pub mod terminal; +pub mod timer; + +pub use yggdrasil_rt::io::{FileMetadataUpdate, FileMetadataUpdateMode, FileMode as RawFileMode}; + +pub fn update_metadata>( + path: P, + update: &FileMetadataUpdate, +) -> crate::io::Result<()> { + let path = path.as_ref().to_str().unwrap(); + unsafe { yggdrasil_rt::sys::update_metadata(None, path, &update) }?; + Ok(()) +} diff --git a/library/std/src/os/yggdrasil/io/net/mod.rs b/library/std/src/os/yggdrasil/io/net/mod.rs new file mode 100644 index 00000000000..8569154e11c --- /dev/null +++ b/library/std/src/os/yggdrasil/io/net/mod.rs @@ -0,0 +1 @@ +pub mod raw_socket; diff --git a/library/std/src/os/yggdrasil/io/net/raw_socket.rs b/library/std/src/os/yggdrasil/io/net/raw_socket.rs new file mode 100644 index 00000000000..3d7821e2d1a --- /dev/null +++ b/library/std/src/os/yggdrasil/io/net/raw_socket.rs @@ -0,0 +1,41 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::net::{self as rt, SocketInterfaceQuery}; + +use crate::io; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub struct RawSocket(FileDesc); + +impl RawSocket { + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn bind<'a, Q: Into>>(interface: Q) -> io::Result { + let query = interface.into(); + let raw = rt::bind_raw(query)?; + let inner = unsafe { FileDesc::from_raw_fd(raw) }; + Ok(Self(inner)) + } + + pub fn send(&self, data: &[u8]) -> io::Result { + Ok(rt::socket::send(self.as_raw_fd(), data)?) + } + + pub fn recv(&self, data: &mut [u8]) -> io::Result { + Ok(rt::socket::receive(self.as_raw_fd(), data)?) + } + + pub fn hardware_address(&self) -> io::Result<[u8; 6]> { + rt::get_socket_option!(self.as_raw_fd(), rt::options::BoundHardwareAddress) + .map(Into::into) + .map_err(io::Error::from) + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl AsRawFd for RawSocket { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/os/yggdrasil/io/pipe.rs b/library/std/src/os/yggdrasil/io/pipe.rs new file mode 100644 index 00000000000..8cbeb14d021 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/pipe.rs @@ -0,0 +1,17 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::io as rt; + +use crate::os::fd::{FromRawFd, OwnedFd}; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn create_pipe_pair( + read_nonblocking: bool, + write_nonblocking: bool, +) -> crate::io::Result<(OwnedFd, OwnedFd)> { + let (read, write) = rt::create_pipe_pair(read_nonblocking, write_nonblocking)?; + let read = unsafe { OwnedFd::from_raw_fd(read) }; + let write = unsafe { OwnedFd::from_raw_fd(write) }; + + Ok((read, write)) +} diff --git a/library/std/src/os/yggdrasil/io/poll.rs b/library/std/src/os/yggdrasil/io/poll.rs new file mode 100644 index 00000000000..04e5646f661 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/poll.rs @@ -0,0 +1,58 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::io::poll as rt; + +use crate::io; +use crate::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd}; +use crate::time::Duration; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub type PollControl = rt::PollControl; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub struct PollChannel(OwnedFd); + +impl PollChannel { + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn new() -> io::Result { + let raw_fd = unsafe { yggdrasil_rt::sys::create_poll_channel() }?; + let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; + Ok(Self(fd)) + } + + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn add(&mut self, fd: RawFd) -> io::Result<()> { + unsafe { + yggdrasil_rt::sys::poll_channel_control(self.0.as_raw_fd(), PollControl::AddFd, fd) + }?; + Ok(()) + } + + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn remove(&mut self, fd: RawFd) -> io::Result<()> { + unsafe { + yggdrasil_rt::sys::poll_channel_control(self.0.as_raw_fd(), PollControl::RemoveFd, fd) + }?; + Ok(()) + } + + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn wait( + &mut self, + timeout: Option, + retain: bool, + ) -> io::Result)>> { + let mut output = None; + unsafe { + yggdrasil_rt::sys::poll_channel_wait(self.0.as_raw_fd(), &timeout, retain, &mut output) + }?; + Ok(output.map(|(l, r)| (l, r.map_err(io::Error::from)))) + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl AsRawFd for PollChannel { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/os/yggdrasil/io/shared_memory.rs b/library/std/src/os/yggdrasil/io/shared_memory.rs new file mode 100644 index 00000000000..d614544a973 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/shared_memory.rs @@ -0,0 +1,43 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::sys as syscall; + +use crate::io; +use crate::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd}; +use crate::os::yggdrasil::io::mapping::FileMapping; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub struct SharedMemory(OwnedFd, usize); + +impl SharedMemory { + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn new(size: usize) -> io::Result { + let raw_fd = unsafe { syscall::create_shared_memory(size) }?; + let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; + Ok(Self(fd, size)) + } + + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn size(&self) -> usize { + self.1 + } + + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn into_mapping<'a>(self) -> io::Result> { + FileMapping::new(self.0, 0, self.1) + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl Into for SharedMemory { + fn into(self) -> OwnedFd { + self.0 + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl AsRawFd for SharedMemory { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/os/yggdrasil/io/terminal.rs b/library/std/src/os/yggdrasil/io/terminal.rs new file mode 100644 index 00000000000..9c9d2fbc0b0 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/terminal.rs @@ -0,0 +1,84 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::io::{FileMode, OpenOptions, terminal as rt}; +use yggdrasil_rt::process::ProcessGroupId; +use yggdrasil_rt::sys as syscall; + +use crate::fs::File; +use crate::io; +use crate::mem::MaybeUninit; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::path::Path; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub type TerminalOptions = rt::TerminalOptions; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub type TerminalSize = rt::TerminalSize; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub unsafe fn set_terminal_options(fd: &F, opt: &TerminalOptions) -> io::Result<()> { + rt::set_terminal_options(fd.as_raw_fd(), opt)?; + Ok(()) +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub unsafe fn get_terminal_options(fd: &F) -> io::Result { + Ok(rt::get_terminal_options(fd.as_raw_fd())?) +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub unsafe fn set_terminal_group(fd: &F, group: ProcessGroupId) -> io::Result<()> { + Ok(rt::set_terminal_group(fd.as_raw_fd(), group)?) +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn get_terminal_size(fd: &F) -> io::Result { + Ok(rt::get_terminal_size(fd.as_raw_fd())?) +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub unsafe fn update_terminal_options TerminalOptions>( + fd: &F, + mutator: M, +) -> io::Result { + let old = get_terminal_options(fd)?; + let new = mutator(old); + set_terminal_options(fd, &new)?; + Ok(old) +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub unsafe fn start_terminal_session>(terminal: P) -> io::Result<()> { + let terminal = terminal.as_ref().to_str().unwrap(); + + syscall::start_session()?; + + let stdin_fd = syscall::open(None, terminal, OpenOptions::READ, FileMode::empty())?; + let stdout_stderr_fd = syscall::open(None, terminal, OpenOptions::WRITE, FileMode::empty())?; + + syscall::clone_fd(stdin_fd, Some(RawFd::STDIN))?; + syscall::clone_fd(stdout_stderr_fd, Some(RawFd::STDOUT))?; + syscall::clone_fd(stdout_stderr_fd, Some(RawFd::STDERR))?; + + syscall::close(stdin_fd)?; + syscall::close(stdout_stderr_fd)?; + + Ok(()) +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn create_pty( + options: Option, + size: TerminalSize, +) -> io::Result<(File, File)> { + let options = options.unwrap_or_default(); + let mut fds = [MaybeUninit::uninit(), MaybeUninit::uninit()]; + + unsafe { syscall::create_pty(&options, &size, &mut fds) }?; + + let master = unsafe { File::from_raw_fd(fds[0].assume_init()) }; + let slave = unsafe { File::from_raw_fd(fds[1].assume_init()) }; + + Ok((master, slave)) +} diff --git a/library/std/src/os/yggdrasil/io/timer.rs b/library/std/src/os/yggdrasil/io/timer.rs new file mode 100644 index 00000000000..f452a4a0d22 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/timer.rs @@ -0,0 +1,50 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use yggdrasil_rt::io::TimerOptions; +use yggdrasil_rt::sys as syscall; + +use crate::io; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::time::Duration; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub struct TimerFd(FileDesc); + +impl TimerFd { + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn new(repeat: bool, nonblocking: bool) -> io::Result { + let mut options = TimerOptions::empty(); + if repeat { + options |= TimerOptions::REPEAT; + } + let raw = unsafe { syscall::create_timer(options) }?; + let fd = unsafe { FileDesc::from_raw_fd(raw) }; + if nonblocking { + fd.set_nonblocking(true)?; + } + Ok(Self(fd)) + } + + pub fn start(&mut self, timeout: Duration) -> io::Result<()> { + let tval = timeout.as_micros(); + unsafe { syscall::write(self.0.as_raw_fd(), &tval.to_ne_bytes()) }?; + Ok(()) + } + + pub fn is_expired(&mut self) -> io::Result { + let mut buf = [0; 1]; + match unsafe { syscall::read(self.0.as_raw_fd(), &mut buf) }.map_err(io::Error::from) { + Ok(_) => Ok(true), + Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(false), + Err(e) => Err(e), + } + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl AsRawFd for TimerFd { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/os/yggdrasil/mod.rs b/library/std/src/os/yggdrasil/mod.rs new file mode 100644 index 00000000000..d385096060a --- /dev/null +++ b/library/std/src/os/yggdrasil/mod.rs @@ -0,0 +1,34 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] +#![allow(exported_private_dependencies)] + +pub use yggdrasil_rt as rt; + +pub mod fs; +pub mod io; +pub mod process; +pub mod signal; + +use yggdrasil_rt::process::ProgramArgumentInner; +use yggdrasil_rt::sys as syscall; + +use crate::path::{Path, PathBuf}; + +pub(crate) static mut REAL_BINARY_PATH: Option = None; + +pub(crate) unsafe fn set_real_program(program_arg: &ProgramArgumentInner) { + if let Some(real_program) = program_arg.real_program() { + let real_program_str = real_program.to_str().expect("Invalid real_program from kernel"); + REAL_BINARY_PATH = Some(PathBuf::from(real_program_str)); + } +} + +pub fn real_binary_path() -> &'static Path { + #[allow(static_mut_refs)] + unsafe { + REAL_BINARY_PATH.as_deref().unwrap_or_else(|| Path::new("")) + } +} + +pub fn get_random(buffer: &mut [crate::mem::MaybeUninit]) { + unsafe { syscall::get_random(buffer.assume_init_mut()) } +} diff --git a/library/std/src/os/yggdrasil/process.rs b/library/std/src/os/yggdrasil/process.rs new file mode 100644 index 00000000000..f9f452789e9 --- /dev/null +++ b/library/std/src/os/yggdrasil/process.rs @@ -0,0 +1,100 @@ +#![stable(feature = "os", since = "1.0.0")] + +use yggdrasil_rt::io::RawFd; +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub use yggdrasil_rt::process::ProcessGroupId; +use yggdrasil_rt::process::{ProcessId, Signal, SpawnFlags}; +use yggdrasil_rt::sys as syscall; + +use crate::io; +use crate::path::Path; +use crate::process::{Child, Command, ExitStatus}; +use crate::sealed::Sealed; +use crate::sys::{AsInner, AsInnerMut}; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub trait ChildExt: Sealed { + #[unstable(feature = "yggdrasil_os", issue = "none")] + fn main_thread_id(&self) -> io::Result; +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub trait CommandExt: Sealed { + #[unstable(feature = "yggdrasil_os", issue = "none")] + fn process_group(&mut self, pgroup: ProcessGroupId) -> &mut Command; + + #[unstable(feature = "yggdrasil_os", issue = "none")] + unsafe fn gain_terminal>(&mut self, fd: F) -> &mut Command; + + #[unstable(feature = "yggdrasil_os", issue = "none")] + unsafe fn attach_tracing(&mut self) -> &mut Command; + + #[unstable(feature = "yggdrasil_os", issue = "none")] + fn disable_aslr(&mut self) -> &mut Command; + + #[unstable(feature = "yggdrasil_os", issue = "none")] + fn change_root>(&mut self, new: P) -> &mut Command; +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub trait ExitStatusExt: Sealed { + #[unstable(feature = "yggdrasil_os", issue = "none")] + fn signal(&self) -> Option>; +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl CommandExt for Command { + fn process_group(&mut self, pgroup: ProcessGroupId) -> &mut Command { + self.as_inner_mut().pgroup = Some(pgroup); + self + } + + unsafe fn gain_terminal>(&mut self, fd: F) -> &mut Command { + self.as_inner_mut().gain_terminal = Some(fd.into()); + self + } + + unsafe fn attach_tracing(&mut self) -> &mut Command { + self.as_inner_mut().attach_trace = true; + self + } + + fn disable_aslr(&mut self) -> &mut Command { + self.as_inner_mut().flags |= SpawnFlags::DISABLE_ASLR; + self + } + + fn change_root>(&mut self, new: P) -> &mut Command { + self.as_inner_mut().change_root(new); + self + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl ExitStatusExt for ExitStatus { + fn signal(&self) -> Option> { + self.as_inner().signal() + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl ChildExt for Child { + fn main_thread_id(&self) -> io::Result { + self.as_inner().main_thread_id() + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn id_ext() -> ProcessId { + unsafe { syscall::get_pid() } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn group_id() -> ProcessGroupId { + unsafe { syscall::get_process_group_id() } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub fn create_process_group() -> ProcessGroupId { + unsafe { syscall::create_process_group() } +} diff --git a/library/std/src/os/yggdrasil/signal.rs b/library/std/src/os/yggdrasil/signal.rs new file mode 100644 index 00000000000..0ef8dea21f8 --- /dev/null +++ b/library/std/src/os/yggdrasil/signal.rs @@ -0,0 +1,27 @@ +pub use yggdrasil_rt::process::{ProcessId, Signal}; +pub use yggdrasil_rt::sys as syscall; + +use crate::io; + +#[must_use = "It is adviced to store the original handler in case it needs to be restored later"] +#[derive(Clone, Copy)] +pub enum SignalHandler { + Ignore, + Terminate, + Function(fn(Signal)), +} + +pub fn set_signal_handler(signal: Signal, handler: SignalHandler) -> SignalHandler { + crate::sys::signal::set_signal_handler(signal, handler) +} + +pub fn send(destination: u32, signal: Signal) -> io::Result<()> { + let destination = unsafe { ProcessId::from_raw(destination) }; + unsafe { syscall::send_signal(destination, signal) }?; + Ok(()) +} + +pub fn abort() -> ! { + unsafe { syscall::send_signal(syscall::get_pid(), Signal::Aborted) }.expect("Could not abort"); + unreachable!(); +} diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index f2f1d1c7fec..73e9f5346e6 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -107,4 +107,7 @@ cfg_select! { target_os = "zkvm" => { mod zkvm; } + target_os = "yggdrasil" => { + mod yggdrasil; + } } diff --git a/library/std/src/sys/alloc/yggdrasil.rs b/library/std/src/sys/alloc/yggdrasil.rs new file mode 100644 index 00000000000..cb54e4054ec --- /dev/null +++ b/library/std/src/sys/alloc/yggdrasil.rs @@ -0,0 +1,49 @@ +use libyalloc::allocator::BucketAllocator; +use libyalloc::sys::OsPageProvider; + +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::cell::UnsafeCell; +use crate::ptr::NonNull; +use crate::sys::sync::Mutex; + +struct Global { + lock: Mutex, + inner: UnsafeCell>, +} + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let ptr = unsafe { + ALLOC.lock.lock(); + let ptr = (*ALLOC.inner.get()).allocate(layout); + ALLOC.lock.unlock(); + ptr + }; + + if let Some(ptr) = ptr { ptr.as_ptr() } else { crate::ptr::null_mut() } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let ptr = NonNull::new(ptr).expect("Invalid pointer"); + + unsafe { + ALLOC.lock.lock(); + (*ALLOC.inner.get()).free(ptr, layout); + ALLOC.lock.unlock(); + } + } +} + +impl Global { + #[rustc_const_unstable(feature = "yggdrasil_os", issue = "none")] + const fn new() -> Self { + Self { lock: Mutex::new(), inner: UnsafeCell::new(BucketAllocator::new()) } + } +} + +unsafe impl Sync for Global {} + +static ALLOC: Global = Global::new(); diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index 5424d40a158..e962efe830b 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -53,6 +53,10 @@ cfg_select! { mod zkvm; pub use zkvm::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::*; + } _ => { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/args/yggdrasil.rs b/library/std/src/sys/args/yggdrasil.rs new file mode 100644 index 00000000000..54fc0f0fb3a --- /dev/null +++ b/library/std/src/sys/args/yggdrasil.rs @@ -0,0 +1,74 @@ +use crate::ffi::OsString; +use crate::{fmt, vec}; + +#[allow(dead_code)] +#[derive(Clone)] +pub struct Args { + iter: vec::IntoIter, +} + +pub fn args() -> Args { + Args { iter: imp::clone() } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.iter, f) + } +} + +impl Iterator for Args { + type Item = OsString; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +pub(crate) mod imp { + use yggdrasil_rt::process::ProgramArgumentInner; + + use crate::ffi::{OsStr, OsString}; + use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::{ptr, vec}; + + static ARGS: AtomicPtr> = AtomicPtr::new(ptr::null_mut()); + + pub(crate) fn init(arg: &ProgramArgumentInner) { + let mut args = Box::new(Vec::new()); + + for arg in arg.args() { + let arg = unsafe { OsStr::from_encoded_bytes_unchecked(arg.to_bytes()) }; + args.push(arg.to_owned()); + } + + let args = Box::into_raw(args); + ARGS.store(args, Ordering::Release); + } + + pub(super) fn clone() -> vec::IntoIter { + unsafe { + ARGS.load(Ordering::Acquire) + .as_ref() + .expect("Program arguments not yet initialized") + .clone() + .into_iter() + } + } +} diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs index 89856516b6d..80bb7fc2d3d 100644 --- a/library/std/src/sys/env/mod.rs +++ b/library/std/src/sys/env/mod.rs @@ -55,6 +55,10 @@ cfg_select! { mod zkvm; pub use zkvm::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::*; + } _ => { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/env/yggdrasil.rs b/library/std/src/sys/env/yggdrasil.rs new file mode 100644 index 00000000000..38659da8e0a --- /dev/null +++ b/library/std/src/sys/env/yggdrasil.rs @@ -0,0 +1,107 @@ +use crate::collections::hash_map; +use crate::ffi::{OsStr, OsString}; +use crate::sys::AsInner; +use crate::{fmt, io}; + +pub struct Env { + iter: hash_map::IntoIter, +} + +impl !Send for Env {} +impl !Sync for Env {} + +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.iter, f) + } +} + +pub fn env() -> Env { + let iter = imp::read().clone().into_iter(); + Env { iter } +} + +pub fn getenv(name: &OsStr) -> Option { + if name.as_inner().as_encoded_bytes().contains(&b'=') { + return None; + } + let env = imp::read(); + env.get(name).cloned() +} + +pub unsafe fn setenv(name: &OsStr, value: &OsStr) -> io::Result<()> { + if name.as_inner().as_encoded_bytes().contains(&b'=') { + return Err(io::Error::new(io::ErrorKind::Uncategorized, "Invalid env var name")); + } + let mut env = imp::write(); + env.insert(name.to_owned(), value.to_owned()); + Ok(()) +} + +pub unsafe fn unsetenv(name: &OsStr) -> io::Result<()> { + if name.as_inner().as_encoded_bytes().contains(&b'=') { + return Err(io::Error::new(io::ErrorKind::Uncategorized, "Invalid env var name")); + } + let mut env = imp::write(); + env.remove(name); + Ok(()) +} + +pub(crate) mod imp { + use yggdrasil_rt::process::ProgramArgumentInner; + + use crate::collections::HashMap; + use crate::ffi::{OsStr, OsString}; + use crate::ptr; + use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; + + static ENV: AtomicPtr>> = AtomicPtr::new(ptr::null_mut()); + + pub(super) fn read() -> RwLockReadGuard<'static, HashMap> { + unsafe { + ENV.load(Ordering::Acquire) + .as_ref() + .expect("Environment variables not initialized") + .read() + .expect("Could not obtain env read lock") + } + } + + pub(super) fn write() -> RwLockWriteGuard<'static, HashMap> { + unsafe { + ENV.load(Ordering::Acquire) + .as_ref() + .expect("Environment variables not initialized") + .write() + .expect("Could not obtain env write lock") + } + } + + pub(crate) fn init(arg: &ProgramArgumentInner) { + let mut envs = HashMap::new(); + for pair in arg.envs() { + let pair = pair.to_bytes(); + let Some((key, value)) = pair.split_once(|c| *c == b'=') else { continue }; + let key = unsafe { OsStr::from_encoded_bytes_unchecked(key).to_owned() }; + let value = unsafe { OsStr::from_encoded_bytes_unchecked(value).to_owned() }; + + envs.insert(key, value); + } + + let envs = Box::into_raw(Box::new(RwLock::new(envs))); + ENV.store(envs, Ordering::Release); + } +} diff --git a/library/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs index 02d61a62f4e..c305a1dc6ba 100644 --- a/library/std/src/sys/fd/mod.rs +++ b/library/std/src/sys/fd/mod.rs @@ -15,6 +15,10 @@ cfg_select! { mod motor; pub use motor::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::*; + } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; diff --git a/library/std/src/sys/fd/yggdrasil/file.rs b/library/std/src/sys/fd/yggdrasil/file.rs new file mode 100644 index 00000000000..f71099cc2e9 --- /dev/null +++ b/library/std/src/sys/fd/yggdrasil/file.rs @@ -0,0 +1,128 @@ +use yggdrasil_rt::io::{self as rt, FileSync, options}; +use yggdrasil_rt::sys as syscall; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::fs::{self, FileAttr}; +use crate::sys::{AsInner, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct FileDesc { + fd: OwnedFd, +} + +impl FileDesc { + pub(crate) fn set_nonblocking(&self, non_blocking: bool) -> io::Result<()> { + rt::set_file_option::(self.as_raw_fd(), &non_blocking)?; + Ok(()) + } + + pub(crate) fn read(&self, buffer: &mut [u8]) -> io::Result { + Ok(unsafe { syscall::read(self.fd.as_raw_fd(), buffer) }?) + } + + pub(crate) fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub(crate) fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + todo!("FileDesc::read_vectored()") + } + + pub(crate) fn write(&self, buffer: &[u8]) -> io::Result { + Ok(unsafe { syscall::write(self.fd.as_raw_fd(), buffer) }?) + } + + pub(crate) fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + todo!("FileDesc::write_vectored()") + } + + pub(crate) fn read_to_end(&self, buffer: &mut Vec) -> io::Result { + let mut buf = [0; 512]; + let mut bytes_read = 0; + + loop { + let amount = self.read(&mut buf)?; + if amount == 0 { + break; + } + + buffer.extend_from_slice(&buf[..amount]); + bytes_read += amount; + } + + Ok(bytes_read) + } + + pub(crate) fn try_clone(&self) -> io::Result { + let raw = unsafe { syscall::clone_fd(self.as_raw_fd(), None) }?; + Ok(unsafe { Self::from_raw_fd(raw) }) + } + + pub(crate) fn metadata(&self) -> io::Result { + fs::get_metadata_inner(Some(self.as_raw_fd()), "", true) + } + + pub(crate) fn truncate(&self, size: u64) -> io::Result<()> { + unsafe { syscall::truncate(self.as_raw_fd(), size) }?; + Ok(()) + } + + pub(crate) fn datasync(&self) -> io::Result<()> { + unsafe { syscall::fsync(self.as_raw_fd(), FileSync::DATA) }?; + Ok(()) + } + + pub(crate) fn fsync(&self) -> io::Result<()> { + unsafe { syscall::fsync(self.as_raw_fd(), FileSync::METADATA | FileSync::DATA) }?; + Ok(()) + } + + pub(crate) fn seek(&self, pos: SeekFrom) -> io::Result { + let mut output = 0; + unsafe { syscall::seek(self.as_raw_fd(), pos.into(), &mut output) }?; + Ok(output) + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner for FileDesc { + fn from_inner(fd: OwnedFd) -> Self { + Self { fd } + } +} + +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } + } +} + +impl AsInner for FileDesc { + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for FileDesc { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() + } +} diff --git a/library/std/src/sys/fd/yggdrasil/mod.rs b/library/std/src/sys/fd/yggdrasil/mod.rs new file mode 100644 index 00000000000..b9fcfdc28d3 --- /dev/null +++ b/library/std/src/sys/fd/yggdrasil/mod.rs @@ -0,0 +1,22 @@ +#![allow(exported_private_dependencies)] + +use yggdrasil_rt::io::SeekFrom as OsSeekFrom; + +use crate::io::SeekFrom; + +mod file; +mod socket; + +pub use file::FileDesc; +pub(crate) use socket::SocketFileDesc; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl From for OsSeekFrom { + fn from(value: SeekFrom) -> OsSeekFrom { + match value { + SeekFrom::Start(v) => OsSeekFrom::Start(v), + SeekFrom::End(v) => OsSeekFrom::End(v), + SeekFrom::Current(v) => OsSeekFrom::Current(v), + } + } +} diff --git a/library/std/src/sys/fd/yggdrasil/socket.rs b/library/std/src/sys/fd/yggdrasil/socket.rs new file mode 100644 index 00000000000..e21bb78b9a4 --- /dev/null +++ b/library/std/src/sys/fd/yggdrasil/socket.rs @@ -0,0 +1,245 @@ +use yggdrasil_rt::{io as rt_io, net as rt, sys as syscall}; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +#[derive(Debug)] +pub(crate) struct SocketFileDesc { + fd: OwnedFd, +} + +impl SocketFileDesc { + pub(crate) fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &timeout)?; + Ok(()) + } + + pub(crate) fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &timeout)?; + Ok(()) + } + + pub(crate) fn set_linger(&self, _linger: Option) -> io::Result<()> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::set_linger()") + } + + pub(crate) fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &nodelay)?; + Ok(()) + } + + pub(crate) fn set_ttl(&self, ttl: u32) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &ttl)?; + Ok(()) + } + + pub(crate) fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + rt_io::set_file_option::(self.as_raw_fd(), &nonblocking)?; + Ok(()) + } + + pub(crate) fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &only_v6)?; + Ok(()) + } + + pub(crate) fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &broadcast)?; + Ok(()) + } + + pub fn set_multicast_loop_v4(&self, loop_v4: bool) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &loop_v4)?; + Ok(()) + } + + pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &ttl)?; + Ok(()) + } + + pub fn set_multicast_loop_v6(&self, loop_v6: bool) -> io::Result<()> { + rt::set_socket_option::(self.as_raw_fd(), &loop_v6)?; + Ok(()) + } + + pub(crate) fn read_timeout(&self) -> io::Result> { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::RecvTimeout)?) + } + + pub(crate) fn write_timeout(&self) -> io::Result> { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::SendTimeout)?) + } + + pub(crate) fn linger(&self) -> io::Result> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::linger()") + } + + pub(crate) fn nodelay(&self) -> io::Result { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::NoDelay)?) + } + + pub(crate) fn ttl(&self) -> io::Result { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::Ttl)?) + } + + pub(crate) fn only_v6(&self) -> io::Result { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::Ipv6Only)?) + } + + pub(crate) fn broadcast(&self) -> io::Result { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::Broadcast)?) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::MulticastLoopV4)?) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::MulticastTtlV4)?) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + Ok(rt::get_socket_option!(self.as_raw_fd(), rt::options::MulticastLoopV6)?) + } + + pub(crate) fn local_addr(&self) -> io::Result { + Ok(rt::local_address(self.as_raw_fd())?) + } + + pub(crate) fn peer_addr(&self) -> io::Result { + Ok(rt::peer_address(self.as_raw_fd())?) + } + + pub(crate) fn join_multicast_v4( + &self, + _multiaddr: &Ipv4Addr, + _interface: &Ipv4Addr, + ) -> io::Result<()> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::join_multicast_v4()") + } + + pub(crate) fn join_multicast_v6( + &self, + _multiaddr: &Ipv6Addr, + _interface: u32, + ) -> io::Result<()> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::join_multicast_v6()") + } + + pub(crate) fn leave_multicast_v4( + &self, + _multiaddr: &Ipv4Addr, + _interface: &Ipv4Addr, + ) -> io::Result<()> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::leave_multicast_v4()") + } + + pub(crate) fn leave_multicast_v6( + &self, + _multiaddr: &Ipv6Addr, + _interface: u32, + ) -> io::Result<()> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::leave_multicast_v6()") + } + + pub fn take_error(&self) -> io::Result> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::take_error()") + } + + pub(crate) fn accept(&self) -> io::Result<(SocketFileDesc, SocketAddr)> { + let (raw, remote) = rt::socket::accept_ip(self.as_raw_fd())?; + let fd = unsafe { Self::from_raw_fd(raw) }; + Ok((fd, remote)) + } + + pub(crate) fn recv_from(&self, buffer: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + Ok(rt::socket::receive_from_ip(self.as_raw_fd(), buffer)?) + } + + pub(crate) fn recv(&self, buffer: &mut [u8]) -> io::Result { + Ok(rt::socket::receive(self.as_raw_fd(), buffer)?) + } + + pub(crate) fn recv_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::recv_buf()") + } + + pub(crate) fn recv_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::recv_vectored()") + } + + pub(crate) fn peek(&self, _buffer: &mut [u8]) -> io::Result { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::peek()") + } + + pub(crate) fn peek_from(&self, _buffer: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::peek_from()") + } + + pub(crate) fn send(&self, data: &[u8]) -> io::Result { + Ok(rt::socket::send(self.as_raw_fd(), data)?) + } + + pub(crate) fn send_to(&self, data: &[u8], dst: &SocketAddr) -> io::Result { + Ok(rt::socket::send_to_ip(self.as_raw_fd(), data, dst)?) + } + + pub(crate) fn send_vectored(&self, _data: &[IoSlice<'_>]) -> io::Result { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::send_vectored()") + } + + pub(crate) fn shutdown(&self, _how: Shutdown) -> io::Result<()> { + todo!("sys::fd::yggdrasil::socket::SocketFileDesc::shutdown()") + } + + pub(crate) fn try_clone(&self) -> io::Result { + let fd = unsafe { syscall::clone_fd(self.as_raw_fd(), None) }?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } +} + +impl AsFd for SocketFileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl IntoRawFd for SocketFileDesc { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() + } +} + +impl AsRawFd for SocketFileDesc { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl FromRawFd for SocketFileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } + } +} + +impl IntoInner for SocketFileDesc { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl AsInner for SocketFileDesc { + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl FromInner for SocketFileDesc { + fn from_inner(fd: OwnedFd) -> Self { + Self { fd } + } +} diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 0c297c5766b..099660559d0 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -45,6 +45,11 @@ cfg_select! { mod vexos; use vexos as imp; } + target_os = "yggdrasil" => { + mod yggdrasil; + use yggdrasil as imp; + pub(crate) use imp::get_metadata_inner; + } _ => { mod unsupported; use unsupported as imp; diff --git a/library/std/src/sys/fs/yggdrasil/attr.rs b/library/std/src/sys/fs/yggdrasil/attr.rs new file mode 100644 index 00000000000..b49d689ccd2 --- /dev/null +++ b/library/std/src/sys/fs/yggdrasil/attr.rs @@ -0,0 +1,171 @@ +use yggdrasil_rt::io::{ + FileAttr as OsFileAttr, FileMetadataUpdate, FileMetadataUpdateMode, FileMode as OsFileMode, + FileType as OsFileType, +}; +use yggdrasil_rt::time::SystemTime as RtSystemTime; + +use crate::hash::Hash; +use crate::os::fd::RawFd; +use crate::path::Path; +use crate::sys::run_with_path_str; +use crate::sys::time::SystemTime; +use crate::{fmt, io}; + +#[derive(Clone, Copy, PartialEq, Debug)] +pub struct FileAttr(pub(super) OsFileAttr); + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct FilePermissions(pub(super) OsFileMode); + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct FileType(pub(super) OsFileType); + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + pub(crate) atime: Option, + pub(crate) mtime: Option, +} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.0.size + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions(self.0.mode) + } + + pub fn file_type(&self) -> FileType { + FileType(self.0.ty) + } + + pub fn mode_ext(&self) -> OsFileMode { + self.0.mode + } + + pub fn inode(&self) -> Option { + self.0.inode + } + + pub fn block_count(&self) -> u64 { + self.0.block_count + } + + pub fn block_size(&self) -> u64 { + self.0.block_size + } + + pub fn modified(&self) -> io::Result { + let time = SystemTime(self.0.mtime); + Ok(time) + } + + pub fn accessed(&self) -> io::Result { + let time = SystemTime(self.0.atime); + Ok(time) + } + + pub fn created(&self) -> io::Result { + let time = SystemTime(self.0.ctime); + Ok(time) + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + !self.0.contains_any( + OsFileMode::USER_WRITE | OsFileMode::GROUP_WRITE | OsFileMode::OTHER_WRITE, + ) + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.0 &= !(OsFileMode::USER_WRITE | OsFileMode::GROUP_WRITE | OsFileMode::OTHER_WRITE); + } else { + self.0 |= OsFileMode::USER_WRITE | OsFileMode::GROUP_WRITE | OsFileMode::OTHER_WRITE; + } + } +} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0.bits(), f) + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 == OsFileType::Directory + } + + pub fn is_file(&self) -> bool { + self.0 == OsFileType::File + } + + pub fn is_symlink(&self) -> bool { + self.0 == OsFileType::Symlink + } + + pub fn is_char_device(&self) -> bool { + self.0 == OsFileType::Char + } + + pub fn is_block_device(&self) -> bool { + self.0 == OsFileType::Block + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.atime = Some(t.0); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.mtime = Some(t.0); + } +} + +pub(crate) fn get_metadata_inner( + at: Option, + path: &str, + follow: bool, +) -> io::Result { + let mut metadata = crate::mem::MaybeUninit::uninit(); + unsafe { yggdrasil_rt::sys::get_metadata(at, path, &mut metadata, follow) }?; + Ok(unsafe { FileAttr(metadata.assume_init()) }) +} + +pub fn stat(path: &Path) -> io::Result { + run_with_path_str(path, |path_str| get_metadata_inner(None, path_str, true)) +} + +pub fn lstat(path: &Path) -> io::Result { + run_with_path_str(path, |path_str| get_metadata_inner(None, path_str, false)) +} + +pub fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> { + run_with_path_str(path, |path_str| { + unsafe { + yggdrasil_rt::sys::update_metadata( + None, + path_str, + &FileMetadataUpdate::Permissions(FileMetadataUpdateMode::Set(perm.0)), + ) + } + .map_err(io::Error::from) + }) +} + +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + todo!("sys::fs::yggdrasil::set_times()") +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + todo!("sys::fs::yggdrasil::set_times_nofollow()") +} diff --git a/library/std/src/sys/fs/yggdrasil/dir.rs b/library/std/src/sys/fs/yggdrasil/dir.rs new file mode 100644 index 00000000000..4e277e993af --- /dev/null +++ b/library/std/src/sys/fs/yggdrasil/dir.rs @@ -0,0 +1,162 @@ +use yggdrasil_rt::io::{DirectoryEntry as OsDirectoryEntry, FileMode as OsFileMode}; + +use super::{FileAttr, FileType, get_metadata_inner}; +use crate::ffi::OsString; +use crate::mem::MaybeUninit; +use crate::os::fd::{AsRawFd, FromRawFd}; +use crate::path::{Path, PathBuf}; +use crate::str::FromStr; +use crate::sys::fd::FileDesc; +use crate::sys::run_with_path_str; +use crate::{fmt, io}; + +const BUFFER_SIZE: usize = 8; + +pub struct DirEntry { + filename: OsString, + path: PathBuf, + ty: FileType, +} + +pub struct ReadDir { + fd: FileDesc, + path: PathBuf, + // TODO fetch more entries at a time + buffer: [MaybeUninit; BUFFER_SIZE], + buffer_position: usize, + buffer_len: usize, + eof: bool, +} + +#[derive(Debug)] +pub struct DirBuilder { + mode: OsFileMode, +} + +impl ReadDir { + fn open(path: &Path) -> io::Result { + let path_buf = PathBuf::from(path); + let path_str = path.to_str().unwrap(); + let fd = unsafe { yggdrasil_rt::sys::open_directory(None, path_str) }?; + Ok(ReadDir { + fd: unsafe { FileDesc::from_raw_fd(fd) }, + buffer: [MaybeUninit::uninit(); BUFFER_SIZE], + buffer_position: 0, + buffer_len: 0, + path: path_buf, + eof: false, + }) + } + + fn fill_buffer(&mut self) -> io::Result<()> { + // If eof, don't attempt to load anything + // If pos != len, still have some entries + if self.eof || self.buffer_position != self.buffer_len { + return Ok(()); + } + + self.buffer_position = 0; + self.buffer_len = unsafe { + yggdrasil_rt::sys::read_directory_entries(self.fd.as_raw_fd(), &mut self.buffer) + }?; + + if self.buffer_len == 0 { + self.eof = true; + } + + Ok(()) + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + loop { + if let Err(e) = self.fill_buffer() { + break Some(Err(e)); + } + + if self.eof { + return None; + } + + assert_ne!(self.buffer_position, self.buffer_len); + + let entry = unsafe { self.buffer[self.buffer_position].assume_init_ref() }; + self.buffer_position += 1; + + break Some(DirEntry::from_raw(self.path.clone(), entry)); + } + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&*self.path, f) + } +} + +impl DirEntry { + fn from_raw(parent: PathBuf, raw: &OsDirectoryEntry) -> io::Result { + let filename = OsString::from_str(raw.name.as_ref()).unwrap(); + let path = parent.join(filename.clone()); + let ty = match raw.ty { + Some(ty) => FileType(ty), + None => { + let metadata = run_with_path_str(path.as_ref(), |path_str| { + get_metadata_inner(None, path_str, false) + })?; + + metadata.file_type() + } + }; + + Ok(Self { path, filename, ty }) + } + + pub fn path(&self) -> PathBuf { + self.path.clone() + } + + pub fn file_name(&self) -> OsString { + self.filename.clone() + } + + pub fn metadata(&self) -> io::Result { + run_with_path_str(self.path.as_ref(), |path_str| get_metadata_inner(None, path_str, false)) + } + + pub fn file_type(&self) -> io::Result { + Ok(self.ty) + } +} + +impl DirBuilder { + pub fn new() -> Self { + Self { mode: OsFileMode::default_dir() } + } + + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_with_path_str(path, |path_str| { + unsafe { yggdrasil_rt::sys::create_directory(None, path_str, self.mode) } + .map_err(io::Error::from) + }) + } +} + +pub fn readdir(path: &Path) -> io::Result { + ReadDir::open(path) +} + +pub fn rmdir(path: &Path) -> io::Result<()> { + run_with_path_str(path, |path_str| { + yggdrasil_rt::io::remove_directory(None, path_str).map_err(io::Error::from) + }) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + run_with_path_str(path, |path_str| { + yggdrasil_rt::io::remove_directory_recursive(None, path_str).map_err(io::Error::from) + }) +} diff --git a/library/std/src/sys/fs/yggdrasil/file.rs b/library/std/src/sys/fs/yggdrasil/file.rs new file mode 100644 index 00000000000..c8b96530a7a --- /dev/null +++ b/library/std/src/sys/fs/yggdrasil/file.rs @@ -0,0 +1,252 @@ +use yggdrasil_rt::io::{ + FileMetadataUpdate, FileMetadataUpdateMode, FileMode as OsFileMode, FileTimesUpdate, + OpenOptions as OsOpenOptions, +}; + +use super::{FileAttr, FilePermissions, FileTimes}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::Path; +use crate::sys::fd::FileDesc; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct File(FileDesc); + +#[derive(Clone, Copy, Debug)] +pub struct OpenOptions { + pub(crate) options: OsOpenOptions, + pub(crate) mode: OsFileMode, +} + +impl OpenOptions { + pub fn new() -> Self { + Self::default() + } + + pub fn read(&mut self, read: bool) { + if read { + self.options |= OsOpenOptions::READ; + } else { + self.options &= !OsOpenOptions::READ; + } + } + + pub fn write(&mut self, write: bool) { + if write { + self.options |= OsOpenOptions::WRITE; + } else { + self.options &= !OsOpenOptions::WRITE; + } + } + + pub fn append(&mut self, append: bool) { + if append { + self.options |= OsOpenOptions::APPEND; + } else { + self.options &= !OsOpenOptions::APPEND; + } + } + + pub fn truncate(&mut self, truncate: bool) { + if truncate { + self.options |= OsOpenOptions::TRUNCATE; + } else { + self.options &= !OsOpenOptions::TRUNCATE; + } + } + + pub fn create(&mut self, create: bool) { + if create { + self.options |= OsOpenOptions::CREATE; + } else { + self.options &= !OsOpenOptions::CREATE; + } + } + + pub fn create_new(&mut self, create_new: bool) { + if create_new { + self.options |= OsOpenOptions::CREATE_EXCL | OsOpenOptions::CREATE; + } else { + self.options &= !OsOpenOptions::CREATE_EXCL | OsOpenOptions::CREATE; + } + } +} + +impl Default for OpenOptions { + fn default() -> Self { + Self { mode: OsFileMode::default_file(), options: OsOpenOptions::default() } + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = path.as_os_str(); + unsafe { + yggdrasil_rt::sys::open(None, path.to_str().unwrap(), opts.options, opts.mode) + .map(|fd| File::from_raw_fd(fd)) + .map_err(io::Error::from) + } + } + + pub fn file_attr(&self) -> io::Result { + self.0.metadata() + } + + pub fn fsync(&self) -> io::Result<()> { + self.0.fsync() + } + + pub fn datasync(&self) -> io::Result<()> { + self.0.datasync() + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + self.0.truncate(size) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn lock(&self) -> io::Result<()> { + todo!("File::lock()") + } + + pub fn lock_shared(&self) -> io::Result<()> { + todo!("File::lock_shared()") + } + + pub fn try_lock(&self) -> Result<(), crate::fs::TryLockError> { + todo!("File::try_lock()") + } + + pub fn try_lock_shared(&self) -> Result<(), crate::fs::TryLockError> { + todo!("File::try_lock_shared()") + } + + pub fn unlock(&self) -> io::Result<()> { + todo!("File::unlock()") + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn size(&self) -> Option> { + // TODO + None + } + + pub fn tell(&self) -> io::Result { + self.0.seek(SeekFrom::Current(0)) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + self.0.seek(pos) + } + + pub fn duplicate(&self) -> io::Result { + self.0.try_clone().map(Self) + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + unsafe { + yggdrasil_rt::sys::update_metadata( + Some(self.as_raw_fd()), + "", + &FileMetadataUpdate::Permissions(FileMetadataUpdateMode::Set(perm.0)), + ) + } + .map_err(io::Error::from) + } + + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + unsafe { + yggdrasil_rt::sys::update_metadata( + Some(self.as_raw_fd()), + "", + &FileMetadataUpdate::Times(FileTimesUpdate { + atime: times.atime, + mtime: times.mtime, + ctime: None, + }), + ) + } + .map_err(io::Error::from) + } +} + +impl AsInner for File { + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl AsInnerMut for File { + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl FromInner for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} diff --git a/library/std/src/sys/fs/yggdrasil/mod.rs b/library/std/src/sys/fs/yggdrasil/mod.rs new file mode 100644 index 00000000000..575ca9f4b8a --- /dev/null +++ b/library/std/src/sys/fs/yggdrasil/mod.rs @@ -0,0 +1,89 @@ +#![allow(exported_private_dependencies)] + +use yggdrasil_rt::io::{AccessMode as OsAccessMode, Rename}; + +use crate::os::yggdrasil::fs::{MetadataExt, OpenOptionsExt}; +use crate::path::{Path, PathBuf}; +use crate::sys::run_with_path_str; +use crate::{io, os}; + +mod attr; +mod dir; +mod file; + +pub(crate) use attr::get_metadata_inner; +pub use attr::{ + FileAttr, FilePermissions, FileTimes, FileType, lstat, set_perm, set_times, set_times_nofollow, + stat, +}; +pub use dir::{DirBuilder, DirEntry, ReadDir, readdir, remove_dir_all, rmdir}; +pub use file::{File, OpenOptions}; + +pub use crate::sys::fs::common::Dir; + +pub fn unlink(path: &Path) -> io::Result<()> { + run_with_path_str(path, |path_str| { + yggdrasil_rt::io::remove_file(None, path_str).map_err(io::Error::from) + }) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = old.to_str().unwrap(); + let new = new.to_str().unwrap(); + unsafe { + yggdrasil_rt::sys::rename(&Rename { + source_at: None, + destination_at: None, + source: old, + destination: new, + }) + }?; + Ok(()) +} + +pub fn exists(path: &Path) -> io::Result { + run_with_path_str(path, |path| { + unsafe { yggdrasil_rt::sys::check_access(None, path, OsAccessMode::empty()) } + .map_err(io::Error::from) + }) + .map(|_| true) +} + +pub fn readlink(path: &Path) -> io::Result { + // TODO PATH_MAX + let mut buf = [0; 4096]; + let len = run_with_path_str(path, |path_str| { + unsafe { yggdrasil_rt::sys::read_link(None, path_str, &mut buf) }.map_err(io::Error::from) + })?; + // TODO error handling + let path = core::str::from_utf8(&buf[..len]).unwrap(); + Ok(PathBuf::from(path)) +} + +pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { + os::yggdrasil::fs::symlink(original, link) +} + +pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { + Err(io::Error::new(io::ErrorKind::Uncategorized, "Hard links are not implemented")) +} + +pub fn canonicalize(path: &Path) -> io::Result { + run_with_path_str(path, |path_str| { + yggdrasil_rt::io::canonicalize(path_str, |s| s.into()).map_err(io::Error::from) + }) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + let src_stat = from.metadata()?; + let raw_mode = src_stat.mode_ext(); + let mut src = crate::fs::File::open(from)?; + let mut dst = crate::fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .mode_ext(raw_mode) + .open(to)?; + + crate::io::copy(&mut src, &mut dst) +} diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs index d7a0b9b4b30..1edf3697b05 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -39,6 +39,10 @@ cfg_select! { mod xous; pub use xous::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::*; + } any( target_os = "vexos", target_family = "wasm", diff --git a/library/std/src/sys/io/error/yggdrasil.rs b/library/std/src/sys/io/error/yggdrasil.rs new file mode 100644 index 00000000000..c80cb1859f0 --- /dev/null +++ b/library/std/src/sys/io/error/yggdrasil.rs @@ -0,0 +1,67 @@ +use yggdrasil_rt::Error as OsError; + +use crate::io::{self, RawOsError}; + +pub fn errno() -> i32 { + // Errors are not reported via errno + 0 +} + +pub fn is_interrupted(code: i32) -> bool { + decode_error_kind(code) == io::ErrorKind::Interrupted +} + +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + let errno = errno as u32; + match OsError::try_from(errno) { + // I/O kinds + Ok(OsError::DoesNotExist) | Ok(OsError::ProcessNotFound) => io::ErrorKind::NotFound, + Ok(OsError::AlreadyExists) => io::ErrorKind::AlreadyExists, + Ok(OsError::TimedOut) => io::ErrorKind::TimedOut, + Ok(OsError::IsADirectory) => io::ErrorKind::IsADirectory, + Ok(OsError::NotADirectory) => io::ErrorKind::NotADirectory, + Ok(OsError::DirectoryNotEmpty) => io::ErrorKind::DirectoryNotEmpty, + Ok(OsError::Interrupted) => io::ErrorKind::Interrupted, + Ok(OsError::WouldBlock) => io::ErrorKind::WouldBlock, + Ok(OsError::ReadOnly) => io::ErrorKind::ReadOnlyFilesystem, + Ok(OsError::PermissionDenied) => io::ErrorKind::PermissionDenied, + Ok(OsError::CrossDeviceLink) => io::ErrorKind::CrossesDevices, + Ok(OsError::UnrecognizedExecutable) + | Ok(OsError::MissingData) + | Ok(OsError::BufferTooSmall) + | Ok(OsError::QueueFull) => io::ErrorKind::InvalidData, + + // Memory and general + Ok(OsError::OutOfMemory) => io::ErrorKind::OutOfMemory, + Ok(OsError::InvalidArgument) + | Ok(OsError::InvalidMemoryOperation) + | Ok(OsError::InvalidOperation) + | Ok(OsError::UndefinedSyscall) => io::ErrorKind::InvalidInput, + Ok(OsError::NotImplemented) => io::ErrorKind::Unsupported, + + // Network + Ok(OsError::HostUnreachable) => io::ErrorKind::HostUnreachable, + Ok(OsError::NetworkUnreachable) => io::ErrorKind::NetworkUnreachable, + Ok(OsError::ConnectionReset) => io::ErrorKind::ConnectionReset, + Ok(OsError::ConnectionRefused) => io::ErrorKind::ConnectionRefused, + Ok(OsError::NotConnected) => io::ErrorKind::NotConnected, + Ok(OsError::AddrInUse) => io::ErrorKind::AddrInUse, + + // Uncategorized + Ok(OsError::InvalidFile) => io::ErrorKind::Uncategorized, + Err(_) => io::ErrorKind::Uncategorized, + } +} + +pub fn error_string(errno: i32) -> String { + crate::format!("{}", decode_error_kind(errno)) +} + +#[allow(exported_private_dependencies)] +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl From for crate::io::Error { + fn from(e: yggdrasil_rt::Error) -> Self { + let raw_os_error = u32::from(e) as RawOsError; + io::Error::from_raw_os_error(raw_os_error) + } +} diff --git a/library/std/src/sys/io/is_terminal/yggdrasil.rs b/library/std/src/sys/io/is_terminal/yggdrasil.rs new file mode 100644 index 00000000000..eb8e859fade --- /dev/null +++ b/library/std/src/sys/io/is_terminal/yggdrasil.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + yggdrasil_rt::io::terminal::is_terminal(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index b3587ab6369..83207b8de13 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -41,6 +41,10 @@ mod is_terminal { mod motor; pub use motor::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::*; + } _ => { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs index 2f064914a83..daad89dc4c1 100644 --- a/library/std/src/sys/net/connection/mod.rs +++ b/library/std/src/sys/net/connection/mod.rs @@ -29,6 +29,10 @@ cfg_select! { mod uefi; pub use uefi::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::*; + } _ => { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/net/connection/yggdrasil/lookup_host.rs b/library/std/src/sys/net/connection/yggdrasil/lookup_host.rs new file mode 100644 index 00000000000..63183fded55 --- /dev/null +++ b/library/std/src/sys/net/connection/yggdrasil/lookup_host.rs @@ -0,0 +1,189 @@ +use yggdrasil_rt::net::dns::{ + self, DnsClass, DnsMessage, DnsRecordData, DnsReplyCode, DnsType, UdpRequester, +}; + +use crate::fs::File; +use crate::io::{self, BufRead, BufReader}; +use crate::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; +use crate::os::fd::AsRawFd; +use crate::os::yggdrasil::io::poll::PollChannel; +use crate::os::yggdrasil::io::timer::TimerFd; +use crate::str::FromStr; +use crate::sys::pal::util::random; +use crate::time::Duration; + +const TIMEOUT: u64 = 500; +const NAMESERVER: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 53); +const HOSTS_PATH: &str = "/etc/hosts"; + +pub struct LookupHost { + addresses: Vec, + port: u16, +} + +struct DnsRequester { + nameserver: SocketAddr, + + poll: PollChannel, + + timer: TimerFd, + socket: UdpSocket, +} + +impl DnsRequester { + pub fn new(nameserver: SocketAddr) -> io::Result { + let mut poll = PollChannel::new()?; + let timer = TimerFd::new(false, false)?; + let socket = UdpSocket::bind("0.0.0.0:0")?; + + poll.add(timer.as_raw_fd())?; + poll.add(socket.as_raw_fd())?; + + Ok(Self { nameserver, poll, timer, socket }) + } +} + +impl UdpRequester for DnsRequester { + type Error = io::Error; + + fn send_message(&mut self, message: &[u8]) -> io::Result<()> { + self.socket.send_to(message, &self.nameserver)?; + Ok(()) + } + + fn receive_message Option>( + &mut self, + map: F, + ) -> io::Result { + let mut buffer = [0; 2048]; + + self.timer.start(Duration::from_millis(TIMEOUT))?; + + loop { + let (poll_fd, result) = self.poll.wait(None, true)?.unwrap(); + result?; + + match poll_fd { + fd if fd == self.socket.as_raw_fd() => { + let (len, _) = self.socket.recv_from(&mut buffer)?; + + if let Some(message) = map(&buffer[..len]) { + return Ok(message); + } + } + fd if fd == self.timer.as_raw_fd() => { + return Err(io::Error::new(io::ErrorKind::TimedOut, "DNS query timed out")); + } + _ => unreachable!(), + } + } + } +} + +// impl LookupHost { +// pub fn port(&self) -> u16 { +// self.port +// } +// } + +impl Iterator for LookupHost { + type Item = SocketAddr; + + fn next(&mut self) -> Option { + let ip = self.addresses.pop()?; + Some(SocketAddr::new(ip, self.port)) + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result { + use crate::str::FromStr; + + let (hostname, port) = s + .rsplit_once(':') + .ok_or(io::Error::new(io::ErrorKind::InvalidData, "Invalid host:port combination"))?; + let port = + u16::from_str(port).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + Self::try_from((hostname, port)) + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((hostname, port): (&'a str, u16)) -> io::Result { + if let Ok(addresses) = get_addresses_from_hosts(hostname) { + if !addresses.is_empty() { + return Ok(Self { addresses, port }); + } + } + + let addresses = get_addresses_from_dns(hostname)?; + + Ok(Self { addresses, port }) + } +} + +pub fn lookup_host(query: &str, port: u16) -> io::Result { + LookupHost::try_from((query, port)) +} + +fn get_addresses_from_hosts(hostname: &str) -> io::Result> { + let reader = BufReader::new(File::open(HOSTS_PATH)?); + let mut addresses = vec![]; + for line in reader.lines() { + let line = line?; + let line = line.trim(); + if line.starts_with('#') { + continue; + } + let Some((address, host)) = line.split_once(' ') else { + continue; + }; + let address = address.trim(); + let host = host.trim(); + let Ok(address) = IpAddr::from_str(address) else { + continue; + }; + + if host == hostname { + addresses.push(address); + } + } + + Ok(addresses) +} + +fn get_addresses_from_dns(hostname: &str) -> io::Result> { + let xid = random(); + let cookie = random(); + let mut addresses = vec![]; + let mut requester = DnsRequester::new(NAMESERVER)?; + let message = dns::perform_query(&mut requester, hostname, DnsType::A, xid, cookie)?; + + if message.reply_code != DnsReplyCode::NO_ERROR { + return Err(io::Error::new(io::ErrorKind::Other, "DNS server returned error")); + } + + for answer in message.answers { + let Some(name) = answer.name.0.as_ref() else { + continue; + }; + let name = name.trim_end_matches('.'); + if name != hostname { + continue; + } + + match (answer.ty, answer.class, &answer.rdata) { + (DnsType::A, DnsClass::IN, DnsRecordData::A(address)) => { + addresses.push(IpAddr::V4(u32::from(*address).into())); + } + _ => (), + } + } + + Ok(addresses) +} diff --git a/library/std/src/sys/net/connection/yggdrasil/mod.rs b/library/std/src/sys/net/connection/yggdrasil/mod.rs new file mode 100644 index 00000000000..587b3d49fcd --- /dev/null +++ b/library/std/src/sys/net/connection/yggdrasil/mod.rs @@ -0,0 +1,11 @@ +#![allow(exported_private_dependencies)] + +mod lookup_host; +mod tcp_listener; +mod tcp_stream; +mod udp_socket; + +pub use lookup_host::lookup_host; +pub use tcp_listener::TcpListener; +pub use tcp_stream::TcpStream; +pub use udp_socket::UdpSocket; diff --git a/library/std/src/sys/net/connection/yggdrasil/tcp_listener.rs b/library/std/src/sys/net/connection/yggdrasil/tcp_listener.rs new file mode 100644 index 00000000000..a5ad5a71d33 --- /dev/null +++ b/library/std/src/sys/net/connection/yggdrasil/tcp_listener.rs @@ -0,0 +1,100 @@ +use yggdrasil_rt::net as rt; + +use super::TcpStream; +use crate::io; +use crate::net::{SocketAddr, ToSocketAddrs}; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::sys::fd::SocketFileDesc; +use crate::sys::net::connection::each_addr; +use crate::sys::{AsInner, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct TcpListener(SocketFileDesc); + +impl TcpListener { + fn try_bind(addr: &SocketAddr) -> io::Result { + let raw = rt::bind_tcp(addr)?; + let fd = unsafe { SocketFileDesc::from_raw_fd(raw) }; + Ok(Self(fd)) + } + + // pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(addr: A) -> io::Result { + each_addr(addr, Self::try_bind) + } + + pub fn socket_addr(&self) -> io::Result { + self.0.local_addr() + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let (fd, remote) = self.0.accept()?; + let stream = TcpStream::from_inner(fd); + Ok((stream, remote)) + } + + pub fn duplicate(&self) -> io::Result { + self.0.try_clone().map(Self) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + self.0.set_only_v6(only_v6) + } + + pub fn only_v6(&self) -> io::Result { + self.0.only_v6() + } + + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } +} + +impl FromInner for TcpListener { + fn from_inner(fd: SocketFileDesc) -> Self { + Self(fd) + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> SocketFileDesc { + self.0 + } +} + +impl AsInner for TcpListener { + fn as_inner(&self) -> &SocketFileDesc { + &self.0 + } +} + +impl FromRawFd for TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self(SocketFileDesc::from_raw_fd(fd)) + } +} + +impl AsRawFd for TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for crate::net::TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} diff --git a/library/std/src/sys/net/connection/yggdrasil/tcp_stream.rs b/library/std/src/sys/net/connection/yggdrasil/tcp_stream.rs new file mode 100644 index 00000000000..89e358e52f7 --- /dev/null +++ b/library/std/src/sys/net/connection/yggdrasil/tcp_stream.rs @@ -0,0 +1,164 @@ +use yggdrasil_rt::net as rt; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::sys::fd::SocketFileDesc; +use crate::sys::net::connection::each_addr; +use crate::sys::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +#[derive(Debug)] +pub struct TcpStream(SocketFileDesc); + +impl TcpStream { + fn connect_inner(remote: &SocketAddr, timeout: Option) -> io::Result { + let fd = rt::connect_tcp(remote, timeout)?; + let fd = unsafe { SocketFileDesc::from_raw_fd(fd) }; + Ok(Self(fd)) + } + + // pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + pub fn connect(addr: A) -> io::Result { + each_addr(addr, |addr| Self::connect_inner(addr, None)) + } + + pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { + Self::connect_inner(addr, Some(timeout)) + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_read_timeout(dur) + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_write_timeout(dur) + } + + pub fn read_timeout(&self) -> io::Result> { + self.0.read_timeout() + } + + pub fn write_timeout(&self) -> io::Result> { + self.0.write_timeout() + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.0.peek(buf) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.recv(buf) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0.recv_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.recv_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.send(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.send_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn peer_addr(&self) -> io::Result { + self.0.peer_addr() + } + + pub fn socket_addr(&self) -> io::Result { + self.0.local_addr() + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + pub fn duplicate(&self) -> io::Result { + self.0.try_clone().map(Self) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + self.0.set_linger(linger) + } + + pub fn linger(&self) -> io::Result> { + self.0.linger() + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.0.set_nodelay(nodelay) + } + + pub fn nodelay(&self) -> io::Result { + self.0.nodelay() + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } +} + +impl FromInner for TcpStream { + fn from_inner(fd: SocketFileDesc) -> Self { + Self(fd) + } +} + +impl IntoInner for TcpStream { + fn into_inner(self) -> SocketFileDesc { + self.0 + } +} + +impl AsInner for TcpStream { + fn as_inner(&self) -> &SocketFileDesc { + &self.0 + } +} + +impl FromRawFd for TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self(SocketFileDesc::from_raw_fd(fd)) + } +} + +impl AsRawFd for TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for crate::net::TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} diff --git a/library/std/src/sys/net/connection/yggdrasil/udp_socket.rs b/library/std/src/sys/net/connection/yggdrasil/udp_socket.rs new file mode 100644 index 00000000000..fa06b7d5810 --- /dev/null +++ b/library/std/src/sys/net/connection/yggdrasil/udp_socket.rs @@ -0,0 +1,187 @@ +use yggdrasil_rt::net as rt; + +use crate::io; +use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::sys::fd::SocketFileDesc; +use crate::sys::net::connection::each_addr; +use crate::sys::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +#[derive(Debug)] +pub struct UdpSocket(SocketFileDesc); + +impl UdpSocket { + fn try_bind(addr: &SocketAddr) -> io::Result { + let raw = rt::bind_udp(addr)?; + let inner = unsafe { SocketFileDesc::from_raw_fd(raw) }; + Ok(Self(inner)) + } + + // Should succeed on first call + fn try_connect(&self, addr: &SocketAddr) -> io::Result<()> { + rt::connect_udp(self.as_raw_fd(), addr)?; + Ok(()) + } + + pub fn bind(addr: A) -> io::Result { + each_addr(addr, Self::try_bind) + } + + pub fn connect(&self, addr: A) -> io::Result<()> { + each_addr(addr, |addr| self.try_connect(addr)) + } + + pub fn peer_addr(&self) -> io::Result { + self.0.peer_addr() + } + + pub fn socket_addr(&self) -> io::Result { + self.0.local_addr() + } + + pub fn recv(&self, buffer: &mut [u8]) -> io::Result { + self.0.recv(buffer) + } + + pub fn recv_from(&self, buffer: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0.recv_from(buffer) + } + + pub fn send(&self, buffer: &[u8]) -> io::Result { + self.0.send(buffer) + } + + pub fn send_to(&self, buffer: &[u8], dst: &SocketAddr) -> io::Result { + self.0.send_to(buffer, dst) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.0.peek(buf) + } + + pub fn peek_from(&self, buffer: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0.peek_from(buffer) + } + + pub fn duplicate(&self) -> io::Result { + self.0.try_clone().map(Self) + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_read_timeout(dur) + } + + pub fn read_timeout(&self) -> io::Result> { + self.0.read_timeout() + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_write_timeout(dur) + } + + pub fn write_timeout(&self) -> io::Result> { + self.0.write_timeout() + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + self.0.set_broadcast(broadcast) + } + + pub fn broadcast(&self) -> io::Result { + self.0.broadcast() + } + + pub fn set_multicast_loop_v4(&self, loop_v4: bool) -> io::Result<()> { + self.0.set_multicast_loop_v4(loop_v4) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0.multicast_loop_v4() + } + + pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> { + self.0.set_multicast_ttl_v4(ttl) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0.multicast_ttl_v4() + } + + pub fn set_multicast_loop_v6(&self, loop_v6: bool) -> io::Result<()> { + self.0.set_multicast_loop_v6(loop_v6) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + self.0.multicast_loop_v6() + } + + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.0.join_multicast_v6(multiaddr, interface) + } + + pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.0.join_multicast_v4(multiaddr, interface) + } + + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.0.leave_multicast_v6(multiaddr, interface) + } + + pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.0.leave_multicast_v4(multiaddr, interface) + } + + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } +} + +impl FromInner for UdpSocket { + fn from_inner(fd: SocketFileDesc) -> Self { + Self(fd) + } +} + +impl IntoInner for UdpSocket { + fn into_inner(self) -> SocketFileDesc { + self.0 + } +} + +impl AsInner for UdpSocket { + fn as_inner(&self) -> &SocketFileDesc { + &self.0 + } +} + +impl FromRawFd for UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self(SocketFileDesc::from_raw_fd(fd)) + } +} + +impl AsRawFd for UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for crate::net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_raw_fd() + } +} diff --git a/library/std/src/sys/net/hostname/mod.rs b/library/std/src/sys/net/hostname/mod.rs index 8ffe4894d71..e09686346a0 100644 --- a/library/std/src/sys/net/hostname/mod.rs +++ b/library/std/src/sys/net/hostname/mod.rs @@ -7,6 +7,10 @@ cfg_select! { mod windows; pub use windows::hostname; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::hostname; + } _ => { mod unsupported; pub use unsupported::hostname; diff --git a/library/std/src/sys/net/hostname/yggdrasil.rs b/library/std/src/sys/net/hostname/yggdrasil.rs new file mode 100644 index 00000000000..d868f68f32d --- /dev/null +++ b/library/std/src/sys/net/hostname/yggdrasil.rs @@ -0,0 +1,6 @@ +use crate::ffi::OsString; +use crate::io::{Error, Result}; + +pub fn hostname() -> Result { + Err(Error::UNSUPPORTED_PLATFORM) +} diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 88d9d420599..0e4c1afe7a5 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -60,6 +60,10 @@ cfg_select! { mod zkvm; pub use self::zkvm::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use self::yggdrasil::*; + } _ => { mod unsupported; pub use self::unsupported::*; diff --git a/library/std/src/sys/pal/yggdrasil/common.rs b/library/std/src/sys/pal/yggdrasil/common.rs new file mode 100644 index 00000000000..ece6ebcf1df --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/common.rs @@ -0,0 +1,11 @@ +// SAFETY: must be called only once during runtime initialization. +// NOTE: this is not guaranteed to run, for example when Rust code is called externally. +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +pub fn abort_internal() -> ! { + core::intrinsics::abort(); +} diff --git a/library/std/src/sys/pal/yggdrasil/entry.rs b/library/std/src/sys/pal/yggdrasil/entry.rs new file mode 100644 index 00000000000..85e654381e7 --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/entry.rs @@ -0,0 +1,43 @@ +use yggdrasil_rt::process::{ExitCode as OsExitCode, ProgramArgumentInner}; + +use crate::sys::{args, env, signal, thread}; + +unsafe fn init_kernel_arg(program_arg: usize) { + unsafe { + let program_arg = crate::ptr::with_exposed_provenance::(program_arg); + let Some(program_arg) = program_arg.as_ref() else { + yggdrasil_rt::debug_trace!(Error, "Kernel provided NULL argument"); + yggdrasil_rt::sys::exit_process(OsExitCode::Exited(1)); + }; + + // Setup TLS as soon as possible + thread::init(program_arg.auxv()); + + // Setup real binary from auxv + crate::os::yggdrasil::set_real_program(program_arg); + + args::imp::init(program_arg); + env::imp::init(program_arg); + } +} + +#[cfg(not(test))] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __rust_start(program_arg: usize, main_ptr: usize) -> ! { + unsafe { init_kernel_arg(program_arg) }; + + // Initialize signals + signal::init(true); + + if main_ptr != 0 { + let main: unsafe extern "C" fn(isize, *const *const u8) -> i32 = + unsafe { core::mem::transmute(main_ptr) }; + let result = unsafe { main(0, core::ptr::null()) }; + unsafe { yggdrasil_rt::sys::exit_process(OsExitCode::Exited(result)) }; + } + loop {} + // let main_ptr = (main as *const ()).addr(); + // if main_ptr != 0 { + // let result = unsafe { main(0, null()) }; + // } +} diff --git a/library/std/src/sys/pal/yggdrasil/futex.rs b/library/std/src/sys/pal/yggdrasil/futex.rs new file mode 100644 index 00000000000..51ce7f77bc0 --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/futex.rs @@ -0,0 +1,23 @@ +use yggdrasil_rt::process::MutexOperation; +use yggdrasil_rt::sys; + +use crate::sync::atomic::Atomic; +use crate::time::Duration; + +pub type Primitive = u32; +pub type Futex = Atomic; +pub type SmallPrimitive = u32; +pub type SmallFutex = Atomic; + +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { + unsafe { sys::mutex(futex, &MutexOperation::WaitWhileEqual(expected, timeout)).is_ok() } +} + +pub fn futex_wake(futex: &Atomic) -> bool { + unsafe { sys::mutex(futex, &MutexOperation::Wake(1)).ok() }; + true +} + +pub fn futex_wake_all(futex: &Atomic) { + unsafe { sys::mutex(futex, &MutexOperation::Wake(u32::MAX)).ok() }; +} diff --git a/library/std/src/sys/pal/yggdrasil/mod.rs b/library/std/src/sys/pal/yggdrasil/mod.rs new file mode 100644 index 00000000000..f8aee874268 --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/mod.rs @@ -0,0 +1,14 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +pub mod futex; +pub mod os; +pub mod signal; +pub mod time; +pub mod util; + +mod common; +mod entry; +mod path; + +pub use common::*; +pub use path::*; diff --git a/library/std/src/sys/pal/yggdrasil/os.rs b/library/std/src/sys/pal/yggdrasil/os.rs new file mode 100644 index 00000000000..fbf6472a44b --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/os.rs @@ -0,0 +1,108 @@ +use yggdrasil_rt::process::ExitCode as OsExitCode; +use yggdrasil_rt::sys as syscall; + +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::path::{Path, PathBuf}; +use crate::sys::{AsInner, FromInner, os_str, run_with_path_str}; +use crate::{fmt, io, iter, mem, slice}; + +pub struct SplitPaths<'a> { + iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, +} + +#[derive(Debug)] +pub struct JoinPathsError; + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "path segment contains separator `/`") + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "failed to join paths" + } +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { + fn bytes_to_path(b: &[u8]) -> PathBuf { + PathBuf::from(unsafe { mem::transmute::<_, &OsStr>(b) }) + } + fn is_separator(b: &u8) -> bool { + *b == b'/' + } + let unparsed = &unparsed.as_inner().inner; + SplitPaths { + iter: unparsed + .split(is_separator as fn(&u8) -> bool) + .map(bytes_to_path as fn(&[u8]) -> PathBuf), + } +} + +pub fn join_paths(paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + let mut joined = Vec::new(); + + for (i, path) in paths.enumerate() { + let path = &path.as_ref().as_inner().inner; + if i > 0 { + joined.push(b'/') + } + if path.contains(&b'/') { + return Err(JoinPathsError); + } + joined.extend_from_slice(path); + } + Ok(FromInner::from_inner(os_str::Buf { inner: joined })) +} + +pub fn current_exe() -> io::Result { + Ok(yggdrasil_rt::io::current_exe(|s| s.into())?) +} + +pub fn home_dir() -> Option { + yggdrasil_rt::io::home_directory(|s| s.into()).ok() +} + +pub fn temp_dir() -> PathBuf { + // TODO prefix by pid + let mut template = *b"/tmp/dXXXXXXXXX"; + yggdrasil_rt::io::make_temp_directory(&mut template) + .expect("Could not set up a temporary directory"); + PathBuf::from(core::str::from_utf8(&template).unwrap()) +} + +pub fn getcwd() -> io::Result { + Ok(yggdrasil_rt::io::current_directory(|s| s.into())?) +} + +pub fn chdir(path: &Path) -> io::Result<()> { + run_with_path_str(path, |p| yggdrasil_rt::io::set_current_directory(p).map_err(io::Error::from)) +} + +pub fn exit(code: i32) -> ! { + let code = OsExitCode::Exited(code); + unsafe { syscall::exit_process(code) } +} + +pub fn getpid() -> u32 { + unsafe { syscall::get_pid() }.into_raw() +} diff --git a/library/std/src/sys/pal/yggdrasil/path.rs b/library/std/src/sys/pal/yggdrasil/path.rs new file mode 100644 index 00000000000..50281cd25a1 --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/path.rs @@ -0,0 +1,7 @@ +use crate::path::Path; + +#[inline] +pub fn run_with_path_str T>(path: &Path, f: F) -> T { + let path_str = path.to_str().unwrap(); + f(path_str) +} diff --git a/library/std/src/sys/pal/yggdrasil/signal.rs b/library/std/src/sys/pal/yggdrasil/signal.rs new file mode 100644 index 00000000000..5eac8fa13eb --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/signal.rs @@ -0,0 +1,44 @@ +#![allow(exported_private_dependencies)] + +use yggdrasil_rt::process::Signal; +use yggdrasil_rt::process::signal::{self as imp, SignalHandler as RtHandler}; + +use crate::os::yggdrasil::signal::SignalHandler as StdHandler; + +const SIGNAL_STACK_SIZE: usize = 4096 * 8; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl From for RtHandler { + fn from(std: StdHandler) -> RtHandler { + match std { + StdHandler::Ignore => RtHandler::Ignore, + StdHandler::Terminate => RtHandler::Terminate, + StdHandler::Function(function) => RtHandler::Rust(function), + } + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl From for StdHandler { + fn from(rt: RtHandler) -> StdHandler { + match rt { + RtHandler::Ignore => StdHandler::Ignore, + RtHandler::Terminate => StdHandler::Terminate, + RtHandler::Rust(function) => StdHandler::Function(function), + RtHandler::C(_) => unreachable!("std will never set a C signal handler"), + } + } +} + +pub fn set_signal_handler(signal: Signal, handler: StdHandler) -> StdHandler { + imp::set_handler(signal, handler.into()).into() +} + +pub fn init(main: bool) { + if main { + imp::setup_signal_full(SIGNAL_STACK_SIZE) + .expect("Couldn't setup signal handler for the main thread"); + } else { + imp::setup_signal_stack(SIGNAL_STACK_SIZE).expect("Couldn't setup thread signal stack"); + } +} diff --git a/library/std/src/sys/pal/yggdrasil/time.rs b/library/std/src/sys/pal/yggdrasil/time.rs new file mode 100644 index 00000000000..8b3ef1a1bf2 --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/time.rs @@ -0,0 +1,51 @@ +use yggdrasil_rt::time::{self as rt, SystemTime as SysTimespec}; + +use crate::time::Duration; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Instant(pub(crate) SysTimespec); + +impl Instant { + pub fn now() -> Self { + Self(rt::get_monotonic_time().expect("Could not retrieve monotonic time value")) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.0.checked_add_duration(other).map(Self) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + self.0.checked_sub_duration(other).map(Self) + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub_time(&other.0) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct SystemTime(pub(crate) SysTimespec); + +pub const UNIX_EPOCH: SystemTime = SystemTime(SysTimespec::ZERO); + +impl SystemTime { + pub const MAX: SystemTime = SystemTime(SysTimespec::MAX); + + pub const MIN: SystemTime = SystemTime(SysTimespec::MIN); + + pub fn now() -> Self { + Self(rt::get_real_time().expect("Could not retrieve real time value")) + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.sub_time(&other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.0.checked_add_duration(other).map(Self) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + self.0.checked_sub_duration(other).map(Self) + } +} diff --git a/library/std/src/sys/pal/yggdrasil/util.rs b/library/std/src/sys/pal/yggdrasil/util.rs new file mode 100644 index 00000000000..c72f6740bb8 --- /dev/null +++ b/library/std/src/sys/pal/yggdrasil/util.rs @@ -0,0 +1,48 @@ +use crate::env; +use crate::path::PathBuf; + +pub fn resolve_binary(name: &str) -> Option { + // Already an absolute path + if name.starts_with('/') { + return None; + } + + let Ok(path) = env::var("PATH") else { + return None; + }; + + for entry in path.split(':') { + let full_path = PathBuf::from(entry).join(name); + + if full_path.exists() { + return Some(full_path.to_str().unwrap().to_owned()); + } + } + + None +} + +macro_rules! impl_random_safe { + ($($ty:ty),+) => { + $(impl RandomSafe for $ty { + unsafe fn __randomize(&mut self) { + let bytes: &mut [u8; size_of::()] = unsafe { $crate::mem::transmute(self) }; + crate::sys::random::fill_bytes(bytes); + } + })+ + }; +} + +pub trait RandomSafe: Default { + unsafe fn __randomize(&mut self); +} + +impl_random_safe!(u8, u16, u32, u64); + +pub fn random() -> T { + let mut v = T::default(); + unsafe { + v.__randomize(); + } + v +} diff --git a/library/std/src/sys/pipe/mod.rs b/library/std/src/sys/pipe/mod.rs index 85228963b4a..60b48b74524 100644 --- a/library/std/src/sys/pipe/mod.rs +++ b/library/std/src/sys/pipe/mod.rs @@ -13,6 +13,10 @@ cfg_select! { mod motor; pub use motor::{Pipe, pipe}; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::{Pipe, pipe}; + } _ => { mod unsupported; pub use unsupported::{Pipe, pipe}; diff --git a/library/std/src/sys/pipe/yggdrasil.rs b/library/std/src/sys/pipe/yggdrasil.rs new file mode 100644 index 00000000000..1f28b24dbec --- /dev/null +++ b/library/std/src/sys/pipe/yggdrasil.rs @@ -0,0 +1,124 @@ +#![allow(exported_private_dependencies)] + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, PipeReader, PipeWriter}; +use crate::os::fd::{AsRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::os::yggdrasil::io::pipe as os_pipe; +use crate::sys::fd::FileDesc; +use crate::sys::{FromInner, IntoInner}; + +#[derive(Debug)] +pub struct Pipe(FileDesc); + +impl Pipe { + pub fn read(&self, buffer: &mut [u8]) -> io::Result { + self.0.read(buffer) + } + + pub fn read_buf(&self, buffer: BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buffer) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn read_to_end(&self, buffer: &mut Vec) -> io::Result { + self.0.read_to_end(buffer) + } + + pub fn write(&self, buffer: &[u8]) -> io::Result { + self.0.write(buffer) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl AsRawFd for Pipe { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Pipe { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromInner for Pipe { + fn from_inner(fd: FileDesc) -> Self { + Self(fd) + } +} + +impl FromInner for Pipe { + fn from_inner(fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(fd)) + } +} + +impl IntoInner for Pipe { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +#[inline] +pub fn pipe() -> io::Result<(Pipe, Pipe)> { + let (p0, p1) = os_pipe::create_pipe_pair(false, false)?; + Ok((Pipe::from_inner(p0), Pipe::from_inner(p1))) +} + +// pub fn read2(p1: Pipe, v1: &mut Vec, p2: Pipe, v2: &mut Vec) -> io::Result<()> { +// let p1 = p1.into_inner(); +// let p2 = p2.into_inner(); +// +// let mut poll = PollChannel::new()?; +// poll.add(p1.as_raw_fd())?; +// poll.add(p2.as_raw_fd())?; +// +// loop { +// match poll.wait(None, true)? { +// Some((fd, _)) if fd == p1.as_raw_fd() => { +// p1.read_to_end(v1)?; +// p2.read_to_end(v2)?; +// break; +// } +// Some((fd, _)) if fd == p2.as_raw_fd() => { +// p2.read_to_end(v2)?; +// p1.read_to_end(v1)?; +// } +// _ => continue, +// } +// } +// +// Ok(()) +// } + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoRawFd for PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoRawFd for PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index 121d3bc9d5c..246a7a54d16 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -15,6 +15,10 @@ cfg_select! { mod motor; use motor as imp; } + target_os = "yggdrasil" => { + mod yggdrasil; + use yggdrasil as imp; + } _ => { mod unsupported; use unsupported as imp; @@ -43,7 +47,8 @@ pub use imp::{ )) ), target_os = "windows", - target_os = "motor" + target_os = "motor", + target_os = "yggdrasil" ))] pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec)> { let (mut process, mut pipes) = cmd.spawn(Stdio::MakePipe, false)?; @@ -81,6 +86,7 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec< )) ), target_os = "windows", - target_os = "motor" + target_os = "motor", + target_os = "yggdrasil" )))] pub use imp::output; diff --git a/library/std/src/sys/process/yggdrasil/command.rs b/library/std/src/sys/process/yggdrasil/command.rs new file mode 100644 index 00000000000..7abd27e6bc1 --- /dev/null +++ b/library/std/src/sys/process/yggdrasil/command.rs @@ -0,0 +1,208 @@ +use yggdrasil_rt::process::{ + ProcessGroupId as OsProcessGroupId, SpawnFlags, SpawnOption, SpawnOptions, +}; +use yggdrasil_rt::sys as syscall; + +use super::{ChildPipes, Process, Stdio}; +use crate::ffi::OsStr; +use crate::os::fd::RawFd; +use crate::path::{Path, PathBuf}; +use crate::process::StdioPipes; +use crate::sys::process::env::{CommandEnv, CommandEnvs}; +use crate::sys::util; +use crate::{fmt, io}; + +#[derive(Debug)] +pub struct Command { + program: String, + args: Vec, + env: CommandEnv, + cwd: Option, + pub(crate) root: Option, + + pub(crate) pgroup: Option, + pub(crate) gain_terminal: Option, + pub(crate) attach_trace: bool, + pub(crate) flags: SpawnFlags, + + stdin: Stdio, + stdout: Stdio, + stderr: Stdio, +} + +pub struct CommandArgs<'a>(crate::slice::Iter<'a, String>); + +impl Command { + pub fn new(program: &OsStr) -> Self { + let program = program.to_str().unwrap().to_owned(); + + Self { + program, + args: vec![], + env: Default::default(), + cwd: None, + root: None, + pgroup: None, + gain_terminal: None, + attach_trace: false, + flags: SpawnFlags::const_default(), + stdin: Stdio::Inherit, + stdout: Stdio::Inherit, + stderr: Stdio::Inherit, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + if arg.len() == 0 { + self.args.push(String::new()); + } else { + let arg = arg.to_str().unwrap().to_owned(); + self.args.push(arg); + } + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.into()); + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = stdin; + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = stdout; + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = stderr; + } + + pub fn get_program(&self) -> &OsStr { + self.program.as_str().as_ref() + } + + pub fn get_args(&self) -> CommandArgs<'_> { + CommandArgs(self.args.iter()) + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + self.cwd.as_deref() + } + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + // let default_stdin = if needs_stdin { &default } else { &self.stdin }; + + let stdin = &self.stdin; + let stdout = &self.stdout; + let stderr = &self.stderr; + + let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; + let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; + let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; + + let ours = StdioPipes { stdin: our_stdin, stdout: our_stdout, stderr: our_stderr }; + let theirs = ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr }; + + let process = self.do_spawn(&theirs)?; + + Ok((process, ours)) + } + + fn do_spawn(&mut self, pipes: &ChildPipes) -> io::Result { + let mut optional = Vec::new(); + + if let Some(stdin) = pipes.stdin.as_inherit_option(RawFd::STDIN) { + optional.push(stdin); + } + if let Some(stdout) = pipes.stdout.as_inherit_option(RawFd::STDOUT) { + optional.push(stdout); + } + if let Some(stderr) = pipes.stderr.as_inherit_option(RawFd::STDERR) { + optional.push(stderr); + } + + if let Some(pgroup) = self.pgroup { + optional.push(SpawnOption::SetProcessGroup(pgroup)); + } + + if let Some(fd) = self.gain_terminal { + optional.push(SpawnOption::GainTerminal(fd)); + } + + if self.attach_trace { + optional.push(SpawnOption::AttachTrace); + } + + let program = self.program.as_str(); + let arguments = &Vec::from_iter( + crate::iter::once(self.program.as_str()) + .chain(self.args.iter().map(|arg| arg.as_str())), + ); + + let program = &util::resolve_binary(program).unwrap_or_else(|| program.to_owned()); + + let envs = Vec::from_iter(self.env.capture().iter().filter_map(|(key, value)| { + let key = key.to_str()?; + let value = value.to_str()?; + Some(format!("{}={}", key, value)) + })); + + let directory = self.cwd.as_ref().map(|cwd| cwd.to_str().unwrap()); + let root = self.root.as_ref().map(|root| root.to_str().unwrap()); + + let environment = &Vec::from_iter(envs.iter().map(|x| x.as_str())); + + let options = SpawnOptions { + program, + arguments, + environment, + directory, + root, + optional: &optional, + flags: self.flags, + }; + + let pid = unsafe { syscall::spawn_process(&options) }?; + + Ok(Process { pid }) + } + + pub(crate) fn change_root>(&mut self, root: P) { + self.root = Some(root.as_ref().into()); + } +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.0.next().map(|s| s.as_ref()) + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> {} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} diff --git a/library/std/src/sys/process/yggdrasil/mod.rs b/library/std/src/sys/process/yggdrasil/mod.rs new file mode 100644 index 00000000000..7d21c07dfc7 --- /dev/null +++ b/library/std/src/sys/process/yggdrasil/mod.rs @@ -0,0 +1,92 @@ +#![allow(exported_private_dependencies)] + +use yggdrasil_rt::process::{ + self as rt, ExitCode as OsExitCode, ProcessId as OsProcessId, ProcessWait, Signal as OsSignal, + WaitFlags, +}; +use yggdrasil_rt::sys as syscall; + +use crate::io; +use crate::os::fd::AsRawFd; +use crate::os::yggdrasil::io::poll::PollChannel; + +mod command; +mod status; +mod stdio; + +pub use command::{Command, CommandArgs}; +pub use status::{ExitCode, ExitStatus, ExitStatusError}; +pub use stdio::{ChildPipes, Stdio}; + +pub use crate::ffi::OsString as EnvKey; + +// Process + +pub struct Process { + pid: OsProcessId, +} + +impl Process { + pub fn id(&self) -> u32 { + self.pid.into_raw() + } + + pub fn main_thread_id(&self) -> io::Result { + Ok(rt::get_process_option!(Some(self.pid), rt::options::MainThread)?.into_raw()) + } + + pub fn kill(&mut self) -> io::Result<()> { + unsafe { syscall::send_signal(self.pid, OsSignal::Killed) }?; + Ok(()) + } + + fn wait_impl(&mut self, flags: WaitFlags) -> io::Result { + let mut status = OsExitCode::Exited(0); + unsafe { syscall::wait_process(&ProcessWait::Process(self.pid), &mut status, flags) }?; + Ok(ExitStatus(status)) + } + + pub fn wait(&mut self) -> io::Result { + self.wait_impl(WaitFlags::empty()) + } + + pub fn try_wait(&mut self) -> io::Result> { + match self.wait_impl(WaitFlags::NON_BLOCKING) { + Ok(s) => Ok(Some(s)), + Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None), + Err(e) => Err(e), + } + } +} + +// ChildPipe + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + stdout: &mut Vec, + err: ChildPipe, + stderr: &mut Vec, +) -> io::Result<()> { + let mut poll = PollChannel::new()?; + poll.add(out.as_raw_fd())?; + poll.add(err.as_raw_fd())?; + + loop { + match poll.wait(None, true)? { + Some((fd, _)) if fd == out.as_raw_fd() => { + out.read_to_end(stdout)?; + err.read_to_end(stderr)?; + break; + } + Some((fd, _)) if fd == err.as_raw_fd() => { + err.read_to_end(stderr)?; + out.read_to_end(stdout)?; + } + _ => continue, + } + } + + Ok(()) +} diff --git a/library/std/src/sys/process/yggdrasil/status.rs b/library/std/src/sys/process/yggdrasil/status.rs new file mode 100644 index 00000000000..056c9397263 --- /dev/null +++ b/library/std/src/sys/process/yggdrasil/status.rs @@ -0,0 +1,75 @@ +use yggdrasil_rt::process::{ExitCode as OsExitCode, Signal as OsSignal}; + +use crate::fmt; +use crate::num::NonZeroI32; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ExitStatus(pub(super) OsExitCode); + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct ExitStatusError(pub(super) NonZeroI32); + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct ExitCode(pub(super) bool); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + if let Some(error) = self.0.as_failure() { Err(ExitStatusError(error)) } else { Ok(()) } + } + + pub fn code(&self) -> Option { + match self.0 { + OsExitCode::Exited(code) => Some(code), + _ => None, + } + } + + pub fn signal(&self) -> Option> { + self.0.signal() + } +} + +impl Default for ExitStatus { + fn default() -> Self { + Self(OsExitCode::SUCCESS) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + Some(self.0) + } +} + +impl From for ExitStatus { + fn from(value: ExitStatusError) -> Self { + Self(OsExitCode::from(i32::from(value.0))) + } +} + +impl ExitCode { + pub const SUCCESS: Self = Self(false); + pub const FAILURE: Self = Self(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From for ExitCode { + fn from(value: u8) -> Self { + Self(value != 0) + } +} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} diff --git a/library/std/src/sys/process/yggdrasil/stdio.rs b/library/std/src/sys/process/yggdrasil/stdio.rs new file mode 100644 index 00000000000..1dc40e89df9 --- /dev/null +++ b/library/std/src/sys/process/yggdrasil/stdio.rs @@ -0,0 +1,105 @@ +use yggdrasil_rt::process::SpawnOption; + +use crate::io::{self, Stderr, Stdin, Stdout}; +use crate::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::yggdrasil::io::pipe as os_pipe; +use crate::sys::FromInner; +use crate::sys::fs::File; +use crate::sys::pipe::Pipe; + +#[derive(Debug)] +pub struct ChildPipes { + pub stdin: ChildStdio, + pub stdout: ChildStdio, + pub stderr: ChildStdio, +} + +#[derive(Debug)] +pub enum ChildStdio { + Inherit, + Null, + CopyFd(RawFd), + MoveFd(RawFd), +} + +#[derive(Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, + MoveFd(RawFd), + CopyFd(RawFd), +} + +impl Stdio { + pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { + match self { + Stdio::Inherit => Ok((ChildStdio::Inherit, None)), + Stdio::Null => Ok((ChildStdio::Null, None)), + Stdio::MakePipe => { + let (read, write) = os_pipe::create_pipe_pair(false, false)?; + + if readable { + let write = Pipe::from_inner(write); + Ok((ChildStdio::MoveFd(read.into_raw_fd()), Some(write))) + } else { + let read = Pipe::from_inner(read); + Ok((ChildStdio::MoveFd(write.into_raw_fd()), Some(read))) + } + } + Stdio::MoveFd(fd) => Ok((ChildStdio::MoveFd(*fd), None)), + Stdio::CopyFd(fd) => Ok((ChildStdio::CopyFd(*fd), None)), + } + } +} + +#[allow(ineffective_unstable_trait_impl)] +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl FromRawFd for crate::process::Stdio { + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> crate::process::Stdio { + let io = Stdio::MoveFd(fd); + crate::process::Stdio::from_inner(io) + } +} + +impl From for Stdio { + fn from(pipe: Pipe) -> Stdio { + Self::MoveFd(pipe.into_raw_fd()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Self::MoveFd(file.into_raw_fd()) + } +} + +impl From for Stdio { + fn from(s: Stdin) -> Stdio { + Self::CopyFd(s.as_raw_fd()) + } +} + +impl From for Stdio { + fn from(s: Stdout) -> Stdio { + Self::CopyFd(s.as_raw_fd()) + } +} + +impl From for Stdio { + fn from(s: Stderr) -> Stdio { + Self::CopyFd(s.as_raw_fd()) + } +} + +impl ChildStdio { + pub(super) fn as_inherit_option(&self, child: RawFd) -> Option { + match self { + Self::Inherit => Some(SpawnOption::CopyFile { source: child, child }), + &Self::MoveFd(fd) => Some(SpawnOption::MoveFile { source: fd, child }), + &Self::CopyFd(fd) => Some(SpawnOption::CopyFile { source: fd, child }), + Self::Null => None, + } + } +} diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index 91f72d07387..f7f2024dd54 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -102,6 +102,10 @@ cfg_select! { mod zkvm; pub use zkvm::fill_bytes; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::fill_bytes; + } any( all(target_family = "wasm", target_os = "unknown"), target_os = "xous", diff --git a/library/std/src/sys/random/yggdrasil.rs b/library/std/src/sys/random/yggdrasil.rs new file mode 100644 index 00000000000..1774fdc20f5 --- /dev/null +++ b/library/std/src/sys/random/yggdrasil.rs @@ -0,0 +1,16 @@ +#[inline(always)] +pub fn fill_bytes(buffer: &mut [u8]) { + unsafe { yggdrasil_rt::sys::get_random(buffer) } +} + +// pub fn hashmap_random_keys() -> (u64, u64) { +// const KEY_LEN: usize = crate::mem::size_of::(); +// +// let mut v = [0u8; KEY_LEN * 2]; +// fill_bytes(&mut v); +// +// let key1 = v[0..KEY_LEN].try_into().unwrap(); +// let key2 = v[KEY_LEN..].try_into().unwrap(); +// +// (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) +// } diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 86d0f3fe49c..6eaed9011f6 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -45,6 +45,10 @@ cfg_select! { mod zkvm; pub use zkvm::*; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::*; + } _ => { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/stdio/yggdrasil.rs b/library/std/src/sys/stdio/yggdrasil.rs new file mode 100644 index 00000000000..486beaa989f --- /dev/null +++ b/library/std/src/sys/stdio/yggdrasil.rs @@ -0,0 +1,125 @@ +use yggdrasil_rt::Error as OsError; +use yggdrasil_rt::io::RawFd; + +use crate::io; +use crate::io::{IoSlice, IoSliceMut}; +use crate::sealed::Sealed; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Sealed for Stdin {} + +impl Stdin { + pub const fn new() -> Self { + Self + } +} + +impl Sealed for Stdout {} + +impl Stdout { + pub const fn new() -> Self { + Self + } +} + +impl Sealed for Stderr {} + +impl Stderr { + pub const fn new() -> Self { + Self + } +} + +impl io::Read for Stdin { + fn read(&mut self, data: &mut [u8]) -> io::Result { + Ok(unsafe { yggdrasil_rt::sys::read(RawFd::STDIN, data) }?) + } + + fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { + unimplemented!() + } + + #[inline] + fn is_read_vectored(&self) -> bool { + false + } +} + +impl io::Write for Stdout { + fn write(&mut self, data: &[u8]) -> io::Result { + Ok(unsafe { yggdrasil_rt::sys::write(RawFd::STDOUT, data) }?) + } + + fn write_vectored(&mut self, _data: &[IoSlice<'_>]) -> io::Result { + unimplemented!() + } + + #[inline] + fn is_write_vectored(&self) -> bool { + false + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { + Ok(unsafe { yggdrasil_rt::sys::write(RawFd::STDERR, data) }?) + } + + fn write_vectored(&mut self, _data: &[IoSlice<'_>]) -> io::Result { + unimplemented!() + } + + #[inline] + fn is_write_vectored(&self) -> bool { + false + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 64; + +impl crate::io::IsTerminal for Stdin { + fn is_terminal(&self) -> bool { + yggdrasil_rt::io::terminal::is_terminal(RawFd::STDIN) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(u32::from(OsError::InvalidFile) as i32) +} + +pub struct PanicOutput { + stderr: Stderr, +} + +impl PanicOutput { + pub fn new() -> Self { + Self { stderr: Stderr::new() } + } +} + +impl io::Write for PanicOutput { + fn write(&mut self, buf: &[u8]) -> io::Result { + yggdrasil_rt::debug::trace_raw(yggdrasil_rt::debug::TraceLevel::Error, buf); + self.stderr.write(buf).ok(); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn panic_output() -> Option { + Some(PanicOutput::new()) +} diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs index 83cf0ae6298..24c902b3dfb 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -10,6 +10,7 @@ cfg_select! { target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", + target_os = "yggdrasil", ) => { mod futex; pub use futex::Condvar; diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs index e3d6ad1129c..270398315e2 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -9,6 +9,7 @@ cfg_select! { target_os = "dragonfly", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", + target_os = "yggdrasil", ) => { mod futex; pub use futex::Mutex; diff --git a/library/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs index 8603fca2da5..8fd87668149 100644 --- a/library/std/src/sys/sync/rwlock/mod.rs +++ b/library/std/src/sys/sync/rwlock/mod.rs @@ -9,7 +9,8 @@ cfg_select! { target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", - target_os = "motor", + target_os = "motor", + target_os = "yggdrasil", ) => { mod futex; pub use futex::RwLock; diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index cb6bf6518f8..eea51b71e4d 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -48,6 +48,11 @@ cfg_select! { mod unsupported; pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; } + target_os = "yggdrasil" => { + mod yggdrasil; + pub use yggdrasil::{available_parallelism, sleep, Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + pub(crate) use yggdrasil::init; + } any(target_family = "unix", target_os = "wasi") => { mod unix; pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; diff --git a/library/std/src/sys/thread/yggdrasil.rs b/library/std/src/sys/thread/yggdrasil.rs new file mode 100644 index 00000000000..de29f035c0b --- /dev/null +++ b/library/std/src/sys/thread/yggdrasil.rs @@ -0,0 +1,80 @@ +use yggdrasil_rt::process::{self, AuxValue, ExitCode as OsExitCode, thread as rt, thread_local}; + +use crate::boxed::Box; +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZeroUsize; +use crate::thread::ThreadInit; +use crate::time::Duration; + +#[repr(C)] +pub struct Thread { + inner: rt::ThreadHandle<()>, +} + +pub const DEFAULT_MIN_STACK_SIZE: usize = 8192; + +// TODO put it into OnceLock +pub(super) static mut TLS_IMAGE: Option = None; + +impl Thread { + pub unsafe fn new(stack_size: usize, init: Box) -> io::Result { + // Unused now + let _ = set_name; + let info = rt::ThreadCreateInfo { + signal_stack: rt::ThreadSignalStack::Allocate(4096 * 8), + stack: rt::ThreadStack::Allocate(stack_size), + entry: rt::ThreadFunction::Pointer(Self::entry), + #[allow(static_mut_refs)] + tls_image: TLS_IMAGE.as_ref(), + }; + let inner = rt::Thread::spawn(info, init, false)?; + Ok(Thread { inner }) + } + + pub fn join(self) { + self.inner.join_uninterruptible(); + } + + fn entry(init: Box) { + let start = init.init(); + (start)(); + } +} + +pub fn set_name(name: &CStr) { + rt::Thread::<()>::set_name( + name.to_str().expect("Thread::set_name() expects a valid UTF-8 string"), + ); +} + +pub fn current_os_id() -> Option { + Some(unsafe { yggdrasil_rt::sys::get_tid() as u64 }) +} + +pub fn yield_now() { + yggdrasil_rt::debug_trace!(Error, "thread::yield_now()"); + loop {} +} + +pub fn sleep(duration: Duration) { + process::uninterruptible_sleep(duration) +} + +pub fn available_parallelism() -> io::Result { + yggdrasil_rt::debug_trace!(Error, "thread::available_parallelism()"); + loop {} +} + +pub(crate) unsafe fn init<'a, I: Iterator>(auxv: I) { + // Rust doesn't have a pthread_self, so just reserve two slots in the TCB: + // * self-pointer + // * DTV pointer + let image = thread_local::TlsImage::from_auxv(auxv); + if let Err(error) = thread_local::init_tls(image.as_ref(), false) { + yggdrasil_rt::debug_trace!(Debug, "Could not setup main thread TLS: {error:?}"); + // TODO kill self with ABORTED signal instead + yggdrasil_rt::sys::exit_process(OsExitCode::Exited(1)); + } + TLS_IMAGE = image; +} diff --git a/library/std/src/sys/thread_local/key/yggdrasil.rs b/library/std/src/sys/thread_local/key/yggdrasil.rs new file mode 100644 index 00000000000..31b324c4eef --- /dev/null +++ b/library/std/src/sys/thread_local/key/yggdrasil.rs @@ -0,0 +1,21 @@ +use yggdrasil_rt::process::thread_local as imp; + +pub type Key = usize; + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let dtv = imp::get_dtv(); + dtv.set_specific(key, value.cast(), true); +} + +#[inline] +pub fn create(_dtor: Option) -> Key { + // TODO dtors + let dtv = imp::get_dtv(); + dtv.new_specific() +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + // TODO dtors +} diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index e88011aa22d..820e30e6d9d 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -195,6 +195,15 @@ pub(crate) mod key { pub(super) use moto_rt::tls::{Key, get, set}; use moto_rt::tls::{create, destroy}; } + target_os = "yggdrasil" => { + mod racy; + #[cfg(test)] + mod tests; + mod yggdrasil; + pub(super) use racy::LazyKey; + pub(super) use yggdrasil::{Key, set}; + use yggdrasil::{create, destroy}; + } _ => {} } } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 11f2a28bb93..765f0ca7b67 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -930,29 +930,41 @@ impl Step for StartupObjects { let target = self.target; // Even though no longer necessary on x86_64, they are kept for now to // avoid potential issues in downstream crates. - if !target.is_windows_gnu() { + if !target.is_windows_gnu() && !target.ends_with("-yggdrasil") { return vec![]; } - let mut target_deps = vec![]; + if target.ends_with("-yggdrasil") { + let dst_dir = &builder.native_dir(target).join("rtstartup"); + let sysroot_dir = &builder.sysroot_target_libdir(for_compiler, target); + t!(fs::create_dir_all(dst_dir)); - let src_dir = &builder.src.join("library").join("rtstartup"); - let dst_dir = &builder.native_dir(target).join("rtstartup"); - let sysroot_dir = &builder.sysroot_target_libdir(for_compiler, target); - t!(fs::create_dir_all(dst_dir)); + let src_file = &builder.src.join("library/rtstartup/yggdrasil_rust_entry.rs"); + let dst_file = &dst_dir.join("rust_entry0.o"); + let target_file = sysroot_dir.join("rust_entry0.o"); - for file in &["rsbegin", "rsend"] { - let src_file = &src_dir.join(file.to_string() + ".rs"); - let dst_file = &dst_dir.join(file.to_string() + ".o"); if !up_to_date(src_file, dst_file) { let mut cmd = command(&builder.initial_rustc); + // FIXME(alnyan) do this properly + + let fake_target = if target.starts_with("aarch64-") { + "aarch64-unknown-none" + } else if target.starts_with("x86_64-") { + "x86_64-unknown-none" + } else if target.starts_with("i686-") { + "i686-unknown-linux-gnu" + } else if target.starts_with("riscv64-") { + "riscv64imac-unknown-none-elf" + } else { + unimplemented!("This code shouldn't be reachable"); + }; + cmd.env("RUSTC_BOOTSTRAP", "1"); if !builder.local_rebuild { - // a local_rebuild compiler already has stage1 features cmd.arg("--cfg").arg("bootstrap"); } cmd.arg("--target") - .arg(target.rustc_target_arg()) + .arg(fake_target) .arg("--emit=obj") .arg("-o") .arg(dst_file) @@ -960,12 +972,45 @@ impl Step for StartupObjects { .run(builder); } - let obj = sysroot_dir.join((*file).to_string() + ".o"); - builder.copy_link(dst_file, &obj, FileType::NativeLibrary); - target_deps.push((obj, DependencyType::Target)); - } + builder.copy_link(dst_file, &target_file, FileType::NativeLibrary); + // builder.copy_link(dst_file, &target); + // target_deps.push((target, DependencyType::Target)); - target_deps + vec![(target_file, DependencyType::Target)] + } else { + let mut target_deps = vec![]; + + let src_dir = &builder.src.join("library").join("rtstartup"); + let dst_dir = &builder.native_dir(target).join("rtstartup"); + let sysroot_dir = &builder.sysroot_target_libdir(for_compiler, target); + t!(fs::create_dir_all(dst_dir)); + + for file in &["rsbegin", "rsend"] { + let src_file = &src_dir.join(file.to_string() + ".rs"); + let dst_file = &dst_dir.join(file.to_string() + ".o"); + if !up_to_date(src_file, dst_file) { + let mut cmd = command(&builder.initial_rustc); + cmd.env("RUSTC_BOOTSTRAP", "1"); + if !builder.local_rebuild { + // a local_rebuild compiler already has stage1 features + cmd.arg("--cfg").arg("bootstrap"); + } + cmd.arg("--target") + .arg(target.rustc_target_arg()) + .arg("--emit=obj") + .arg("-o") + .arg(dst_file) + .arg(src_file) + .run(builder); + } + + let obj = sysroot_dir.join((*file).to_string() + ".o"); + builder.copy_link(dst_file, &obj, FileType::NativeLibrary); + target_deps.push((obj, DependencyType::Target)); + } + + target_deps + } } } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 67f4eb37b2c..003e3494c2c 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -38,6 +38,9 @@ pub struct Finder { const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined "riscv64im-unknown-none-elf", + "x86_64-unknown-yggdrasil", + "aarch64-unknown-yggdrasil", + "riscv64-unknown-yggdrasil", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 485af5ab1d0..6a79802cb77 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -444,6 +444,7 @@ impl fmt::Display for Display<'_> { "watchos" => "watchOS", "windows" => "Windows", "visionos" => "visionOS", + "yggdrasil" => "Yggdrasil", _ => "", }, (sym::target_arch, Some(arch)) => match arch.as_str() {