diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 9cdc0801b1f00..04bc7736c3a85 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -571,6 +571,7 @@ impl Target { key!(is_like_msvc, bool); key!(is_like_wasm, bool); key!(is_like_android, bool); + key!(is_like_vexos, bool); key!(default_dwarf_version, u32); key!(allows_weak_linkage, bool); key!(has_rpath, bool); @@ -746,6 +747,7 @@ impl ToJson for Target { target_option_val!(is_like_msvc); target_option_val!(is_like_wasm); target_option_val!(is_like_android); + target_option_val!(is_like_vexos); target_option_val!(default_dwarf_version); target_option_val!(allows_weak_linkage); target_option_val!(has_rpath); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index bfe6f44414717..df5acfba0af65 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1946,6 +1946,8 @@ supported_targets! { ("armv7-sony-vita-newlibeabihf", armv7_sony_vita_newlibeabihf), + ("armv7a-vex-v5", armv7a_vex_v5), + ("armv7-unknown-linux-uclibceabi", armv7_unknown_linux_uclibceabi), ("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf), @@ -2328,6 +2330,9 @@ pub struct TargetOptions { pub is_like_wasm: bool, /// Whether a target toolchain is like Android, implying a Linux kernel and a Bionic libc pub is_like_android: bool, + /// Whether a target toolchain is like VEXos, the operating system used by the VEX Robotics V5 Brain. + /// Introduced for the `armv7a-vex-v5` target. + pub is_like_vexos: bool, /// Default supported version of DWARF on this platform. /// Useful because some platforms (osx, bsd) only want up to DWARF2. pub default_dwarf_version: u32, @@ -2704,6 +2709,7 @@ impl Default for TargetOptions { is_like_msvc: false, is_like_wasm: false, is_like_android: false, + is_like_vexos: false, default_dwarf_version: 4, allows_weak_linkage: true, has_rpath: false, @@ -2811,6 +2817,9 @@ impl Target { Abi::System { unwind } if self.is_like_windows && self.arch == "x86" && !c_variadic => { Abi::Stdcall { unwind } } + Abi::System { unwind } if self.is_like_vexos && self.arch == "arm" && !c_variadic => { + Abi::Aapcs { unwind } + } Abi::System { unwind } => Abi::C { unwind }, Abi::EfiApi if self.arch == "arm" => Abi::Aapcs { unwind: false }, Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false }, diff --git a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs new file mode 100644 index 0000000000000..f8d653e757ef2 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs @@ -0,0 +1,41 @@ +use crate::spec::{ + Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, +}; + +const LINK_SCRIPT: &str = include_str!("./armv7a_vex_v5_linker_script.ld"); + +pub(crate) fn target() -> Target { + Target { + llvm_target: "armv7a-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Armv7-A Cortex-A9 VEX V5 Brain, VEXos".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + options: TargetOptions { + llvm_floatabi: Some(FloatAbi::Hard), + os: "vexos".into(), + is_like_vexos: true, + vendor: "vex".into(), + env: "v5".into(), + cpu: "cortex-a9".into(), + abi: "eabihf".into(), + features: "+v7,+neon,+vfp3d16,+thumb2".into(), + linker: Some("rust-lld".into()), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + link_script: Some(LINK_SCRIPT.into()), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + c_enum_min_bits: Some(8), + max_atomic_width: Some(64), + disable_redzone: true, + emit_debug_gdb_scripts: false, + has_thumb_interworking: true, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5_linker_script.ld b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5_linker_script.ld new file mode 100644 index 0000000000000..319f29a281d5b --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5_linker_script.ld @@ -0,0 +1,100 @@ +OUTPUT_FORMAT("elf32-littlearm") + +ENTRY(_boot) + +__user_ram_start = 0x03800000; +__user_ram_length = 0x04800000; +__user_ram_end = __user_ram_start + __user_ram_length; + +__code_signature_length = 0x20; + +__stack_length = 0x400000; +__heap_end = __user_ram_end - __stack_length; + +/* see https://github.com/llvm/llvm-project/blob/main/libunwind/src/AddressSpace.hpp#L78 */ +__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; +__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; + +MEMORY { + USER_RAM : ORIGIN = __user_ram_start, LENGTH = __user_ram_length +} + +SECTIONS { + /* + * VEXos expects program binaries to have a 32-byte header called a "code signature", + * at their start, which tells the OS that we are a valid program and configures some + * miscellaneous startup behavior. + * + * This section is then initialized as a weak symbol in our PAL. + */ + .code_signature : { + KEEP(*(.code_signature)) + . = __user_ram_start + __code_signature_length; + } > USER_RAM + + .text : { + *(.boot) + *(.text .text.*) + } > USER_RAM + + /* Global/uninitialized/static/constant data sections. */ + .rodata : { + *(.rodata .rodata.*) + } > USER_RAM + + .data : { + *(.data .data.*) + } > USER_RAM + + .bss : { + __bss_start = .; + *(.bss .bss.*) + __bss_end = .; + } > USER_RAM + + /* + * These sections are added by the compiler in some cases to facilitate stack unwinding. + * __eh_frame_start and similar symbols are used by libunwind. + */ + .eh_frame_hdr : { + KEEP(*(.eh_frame_hdr)) + } > USER_RAM + + .eh_frame : { + __eh_frame_start = .; + KEEP(*(.eh_frame)) + __eh_frame_end = .; + } > USER_RAM + + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > USER_RAM + + .ARM.extab : { + __extab_start = .; + *(.ARM.extab*) + __extab_end = .; + } > USER_RAM + + /* Active memory regions for the stack/heap. */ + .heap (NOLOAD) : ALIGN(4) { + __heap_start = .; + . = __heap_end; + } > USER_RAM + + .stack (NOLOAD) : ALIGN(8) { + __stack_bottom = .; + . += __stack_length; + __stack_top = .; + } > USER_RAM + + /* + * `.ARM.attributes` contains arch metadata for compatibility purposes, but we + * only target one hardware configuration, meaning it'd just take up space. + */ + /DISCARD/ : { + *(.ARM.attributes*) + } +} diff --git a/library/Cargo.lock b/library/Cargo.lock index 207c744ee2248..6d4831be304b9 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -333,6 +333,7 @@ dependencies = [ "rustc-demangle", "std_detect", "unwind", + "vex-sdk", "wasi", "windows-targets 0.0.0", ] @@ -401,6 +402,16 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "vex-sdk" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1b586dd8546706564cf20c94bae51c8f9e4891d70360d3fb63d563350df3d1" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/library/backtrace b/library/backtrace index f8cc6ac9acc4e..230570f2dac80 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit f8cc6ac9acc4e663ecd96f9bcf1ff4542636d1b9 +Subproject commit 230570f2dac80a601f5c0b30da00cc9480bd35eb diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index a9d1f53761cbf..e73db243560e9 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -19,3 +19,7 @@ compiler_builtins = "0.1.0" [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2", default-features = false } + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = ['cfg(target_os, values("vexos"))'] diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index dc2b42bb90ae8..6e2b29a41a995 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -51,6 +51,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { all(target_vendor = "fortanix", target_env = "sgx"), target_os = "xous", target_os = "uefi", + target_os = "vexos", ))] { unsafe fn abort() -> ! { // call std::sys::abort_internal diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index e7f7f38cb4154..fe191c72f4b4f 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -63,7 +63,7 @@ path = "../windows_targets" rand = { version = "0.8.5", default-features = false, features = ["alloc"] } rand_xorshift = "0.3.0" -[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] +[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "vexos", target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] @@ -85,6 +85,9 @@ wasi = { version = "0.11.0", features = [ r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } +[target.'cfg(target_os = "vexos")'.dependencies] +vex-sdk = { version = "0.26.0", features = ['rustc-dep-of-std'] } + [features] backtrace = [ 'addr2line/rustc-dep-of-std', @@ -140,6 +143,7 @@ level = "warn" check-cfg = [ 'cfg(bootstrap)', 'cfg(target_arch, values("xtensa"))', + 'cfg(target_os, values("vexos"))', # std use #[path] imports to portable-simd `std_float` crate # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/library/std/build.rs b/library/std/build.rs index 8dc326a3dde6a..e61e59b5fd629 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -56,6 +56,7 @@ fn main() { || target_os == "zkvm" || target_os == "rtems" || target_os == "nuttx" + || target_os == "vexos" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index 2c0b533a5703f..1a41fa9f3adfc 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -90,5 +90,7 @@ cfg_if::cfg_if! { mod xous; } else if #[cfg(target_os = "zkvm")] { mod zkvm; + } else if #[cfg(target_os = "vexos")] { + mod vexos; } } diff --git a/library/std/src/sys/alloc/vexos.rs b/library/std/src/sys/alloc/vexos.rs new file mode 100644 index 0000000000000..e1033f4a62cfd --- /dev/null +++ b/library/std/src/sys/alloc/vexos.rs @@ -0,0 +1,98 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; +use crate::sync::atomic::{AtomicBool, Ordering}; + +static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new_with_allocator(Vexos); + +extern "C" { + static mut __heap_start: u8; + static mut __heap_end: u8; +} + +struct Vexos; + +unsafe impl dlmalloc::Allocator for Vexos { + /// Allocs system resources + fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) { + static INIT: AtomicBool = AtomicBool::new(false); + + if !INIT.swap(true, Ordering::Relaxed) { + unsafe { + ( + ptr::addr_of_mut!(__heap_start).cast(), + ptr::addr_of!(__heap_end).byte_offset_from(ptr::addr_of!(__heap_start)) as _, + 0, + ) + } + } else { + (ptr::null_mut(), 0, 0) + } + } + + fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { + ptr::null_mut() + } + + fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool { + false + } + + fn free(&self, _ptr: *mut u8, _size: usize) -> bool { + false + } + + fn can_release_part(&self, _flags: u32) -> bool { + false + } + + fn allocates_zeros(&self) -> bool { + false + } + + fn page_size(&self) -> usize { + 0x1000 + } +} + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } + } +} + +mod lock { + #[inline] + pub fn lock() {} // we don't have threads, which makes this real easy +} diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 9be018c8a5312..45c692c805c80 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -61,6 +61,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "zkvm")] { mod zkvm; pub use self::zkvm::*; + } else if #[cfg(target_os = "vexos")] { + mod vexos; + pub use self::vexos::*; } else { mod unsupported; pub use self::unsupported::*; diff --git a/library/std/src/sys/pal/vexos/env.rs b/library/std/src/sys/pal/vexos/env.rs new file mode 100644 index 0000000000000..381658cfd342e --- /dev/null +++ b/library/std/src/sys/pal/vexos/env.rs @@ -0,0 +1,9 @@ +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "vexos"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".bin"; + pub const EXE_EXTENSION: &str = "bin"; +} diff --git a/library/std/src/sys/pal/vexos/fs.rs b/library/std/src/sys/pal/vexos/fs.rs new file mode 100644 index 0000000000000..1014bab30bb86 --- /dev/null +++ b/library/std/src/sys/pal/vexos/fs.rs @@ -0,0 +1,589 @@ +use crate::ffi::{CString, OsString}; +use crate::fmt; +use crate::hash::Hash; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +#[derive(Debug)] +struct FileDesc(*mut vex_sdk::FIL); + +pub struct File { + fd: FileDesc, +} + +#[derive(Clone)] +pub struct FileAttr { + size: u64, + is_dir: bool, +} + +#[derive(Debug)] +pub struct ReadDir { + entries: Vec, +} + +#[derive(Debug)] +pub struct DirEntry { + path: PathBuf, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + read: bool, + write: bool, + append: bool, + truncate: bool, + create_new: bool, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes(()); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FilePermissions; + +#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)] +pub struct FileType { + is_dir: bool, +} + +#[derive(Debug)] +pub struct DirBuilder(()); + +impl FileAttr { + /// Creates a FileAttr by getting data from an opened file. + fn from_fd(fd: *mut vex_sdk::FIL) -> io::Result { + let size = unsafe { vex_sdk::vexFileSize(fd) }; + + if size >= 0 { + Ok(Self { size: size as u64, is_dir: false }) + } else { + Err(io::Error::new(io::ErrorKind::InvalidData, "Failed to get file size")) + } + } + + fn from_path(path: &Path) -> io::Result { + let c_path = CString::new(path.as_os_str().as_encoded_bytes()).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "Path contained a null byte") + })?; + + let file_type = unsafe { vex_sdk::vexFileStatus(c_path.as_ptr()) }; + let is_dir = file_type == 3; + + // We can't get the size if its a directory because we cant open it as a file + if is_dir { + Ok(Self { size: 0, is_dir: true }) + } else { + let mut opts = OpenOptions::new(); + opts.read(true); + let file = File::open(path, &opts)?; + let fd = file.fd.0; + + Self::from_fd(fd) + } + } + + pub fn size(&self) -> u64 { + self.size + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions + } + + pub fn file_type(&self) -> FileType { + FileType { is_dir: self.is_dir } + } + + pub fn modified(&self) -> io::Result { + unsupported() + } + + pub fn accessed(&self) -> io::Result { + unsupported() + } + + pub fn created(&self) -> io::Result { + unsupported() + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + false + } + + pub fn set_readonly(&mut self, _readonly: bool) { + panic!("Perimissions do not exist") + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.is_dir + } + + pub fn is_file(&self) -> bool { + !self.is_dir + } + + pub fn is_symlink(&self) -> bool { + false + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.entries.pop().map(Ok) + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.path.clone() + } + + pub fn file_name(&self) -> OsString { + self.path.file_name().unwrap_or(crate::ffi::OsStr::new("")).to_os_string() + } + + pub fn metadata(&self) -> io::Result { + stat(&self.path) + } + + pub fn file_type(&self) -> io::Result { + Ok(self.metadata()?.file_type()) + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { read: false, write: false, append: false, truncate: false, create_new: false } + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + pub fn write(&mut self, write: bool) { + self.write = write; + } + pub fn append(&mut self, append: bool) { + self.append = append; + } + pub fn truncate(&mut self, truncate: bool) { + self.truncate = truncate; + } + pub fn create(&mut self, create: bool) { + self.write = create; + } + pub fn create_new(&mut self, create_new: bool) { + self.create_new = create_new; + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + // Mount sdcard volume as FAT filesystem + map_fresult(unsafe { vex_sdk::vexFileMountSD() })?; + + let path = CString::new(path.as_os_str().as_encoded_bytes()).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "Path contained a null byte") + })?; + + if opts.write && opts.read { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Files cannot be opened with read and write access", + )); + } + if opts.create_new { + let file_exists = unsafe { vex_sdk::vexFileStatus(path.as_ptr()) }; + if file_exists != 0 { + return Err(io::Error::new(io::ErrorKind::AlreadyExists, "File already exists")); + } + } + + let file = if opts.read && !opts.write { + // The second argument to this function is ignored. + // Open in read only mode + unsafe { vex_sdk::vexFileOpen(path.as_ptr(), c"".as_ptr()) } + } else if opts.write && opts.append { + // Open in write and append mode + unsafe { vex_sdk::vexFileOpenWrite(path.as_ptr()) } + } else if opts.write && opts.truncate { + // Open in write mode + unsafe { vex_sdk::vexFileOpenCreate(path.as_ptr()) } + } else if opts.write { + unsafe { + // Open in read/write and append mode + let fd = vex_sdk::vexFileOpenWrite(path.as_ptr()); + // Seek to beginning of the file + vex_sdk::vexFileSeek(fd, 0, 0); + + fd + } + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Files cannot be opened without read or write access", + )); + }; + + if file.is_null() { + Err(io::Error::new(io::ErrorKind::NotFound, "Could not open file")) + } else { + Ok(Self { fd: FileDesc(file) }) + } + } + + pub fn file_attr(&self) -> io::Result { + FileAttr::from_fd(self.fd.0) + } + + pub fn fsync(&self) -> io::Result<()> { + self.flush() + } + + pub fn datasync(&self) -> io::Result<()> { + self.flush() + } + + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> io::Result { + unsupported() + } + + pub fn try_lock_shared(&self) -> io::Result { + unsupported() + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + unsupported() + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let len = buf.len() as _; + let buf_ptr = buf.as_mut_ptr(); + let read = unsafe { vex_sdk::vexFileRead(buf_ptr.cast(), 1, len, self.fd.0) }; + if read < 0 { + Err(io::Error::new(io::ErrorKind::Other, "Could not read from file")) + } else { + Ok(read as usize) + } + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|b| self.read(b), cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = buf.len(); + let buf_ptr = buf.as_ptr(); + let written = + unsafe { vex_sdk::vexFileWrite(buf_ptr.cast_mut().cast(), 1, len as _, self.fd.0) }; + if written < 0 { + Err(io::Error::new(io::ErrorKind::Other, "Could not write to file")) + } else { + Ok(written as usize) + } + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn flush(&self) -> io::Result<()> { + unsafe { + vex_sdk::vexFileSync(self.fd.0); + } + Ok(()) + } + + fn tell(&self) -> io::Result { + let position = unsafe { vex_sdk::vexFileTell(self.fd.0) }; + position.try_into().map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "Failed to get current location in file") + }) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + const SEEK_SET: i32 = 0; + const SEEK_CUR: i32 = 1; + const SEEK_END: i32 = 2; + + fn try_convert_offset>(offset: T) -> io::Result { + offset.try_into().map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidInput, + "Cannot seek to an offset too large to fit in a 32 bit integer", + ) + }) + } + + match pos { + SeekFrom::Start(offset) => unsafe { + map_fresult(vex_sdk::vexFileSeek(self.fd.0, try_convert_offset(offset)?, SEEK_SET))? + }, + SeekFrom::End(offset) => unsafe { + if offset >= 0 { + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + try_convert_offset(offset)?, + SEEK_END, + ))? + } else { + // `vexFileSeek` does not support seeking with negative offset, meaning + // we have to calculate the offset from the end of the file ourselves. + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + try_convert_offset(self.file_attr()?.size as i64 + offset)?, + SEEK_SET, + ))? + } + }, + SeekFrom::Current(offset) => unsafe { + if offset >= 0 { + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + try_convert_offset(offset)?, + SEEK_CUR, + ))? + } else { + // `vexFileSeek` does not support seeking with negative offset, meaning + // we have to calculate the offset from the stream position ourselves. + map_fresult(vex_sdk::vexFileSeek( + self.fd.0, + try_convert_offset((self.tell()? as i64) + offset)?, + SEEK_SET, + ))? + } + }, + } + + Ok(self.tell()?) + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + unsupported() + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + unsupported() + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder(()) + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("File").field("fd", &self.fd.0).finish() + } +} +impl Drop for File { + fn drop(&mut self) { + unsafe { vex_sdk::vexFileClose(self.fd.0) }; + } +} + +pub fn readdir(_p: &Path) -> io::Result { + // While there *is* a userspace function for reading file directories, + // the necessary implementation cannot currently be done cleanly, as + // VEXos does not expose directory length to user programs. + // + // This means that we would need to create a large fixed-length buffer + // and hope that the folder's contents didn't exceed that buffer's length, + // which obviously isn't behavior we want to rely on in the standard library. + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + unsupported() +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(path: &Path) -> io::Result { + let path = CString::new(path.as_os_str().as_encoded_bytes()) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Path contained a null byte"))?; + + let file_exists = unsafe { vex_sdk::vexFileStatus(path.as_ptr()) }; + if file_exists != 0 { Ok(true) } else { Ok(false) } +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(p: &Path) -> io::Result { + FileAttr::from_path(p) +} + +pub fn lstat(p: &Path) -> io::Result { + // Symlinks aren't supported in this filesystem + stat(p) +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + use crate::fs::File; + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + + io::copy(&mut reader, &mut writer) +} + +fn map_fresult(fresult: vex_sdk::FRESULT) -> io::Result<()> { + // VEX presumably uses a derivative of FatFs (most likely the xilffs library) + // for sdcard filesystem functions. + // + // Documentation for each FRESULT originates from here: + // + match fresult { + vex_sdk::FRESULT::FR_OK => Ok(()), + vex_sdk::FRESULT::FR_DISK_ERR => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "internal function reported an unrecoverable hard error", + )), + vex_sdk::FRESULT::FR_INT_ERR => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "internal error in filesystem runtime", + )), + vex_sdk::FRESULT::FR_NOT_READY => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "the storage device could not be prepared to work", + )), + vex_sdk::FRESULT::FR_NO_FILE => { + Err(io::Error::new(io::ErrorKind::NotFound, "could not find the file in the directory")) + } + vex_sdk::FRESULT::FR_NO_PATH => Err(io::Error::new( + io::ErrorKind::NotFound, + "a directory in the path name could not be found", + )), + vex_sdk::FRESULT::FR_INVALID_NAME => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "the given string is invalid as a path name", + )), + vex_sdk::FRESULT::FR_DENIED => Err(io::Error::new( + io::ErrorKind::PermissionDenied, + "the required access for this operation was denied", + )), + vex_sdk::FRESULT::FR_EXIST => Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "an object with the same name already exists in the directory", + )), + vex_sdk::FRESULT::FR_INVALID_OBJECT => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "invalid or null file/directory object", + )), + vex_sdk::FRESULT::FR_WRITE_PROTECTED => Err(io::Error::new( + io::ErrorKind::PermissionDenied, + "a write operation was performed on write-protected media", + )), + vex_sdk::FRESULT::FR_INVALID_DRIVE => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "an invalid drive number was specified in the path name", + )), + vex_sdk::FRESULT::FR_NOT_ENABLED => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "work area for the logical drive has not been registered", + )), + vex_sdk::FRESULT::FR_NO_FILESYSTEM => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "valid FAT volume could not be found on the drive", + )), + vex_sdk::FRESULT::FR_MKFS_ABORTED => { + Err(io::Error::new(io::ErrorKind::Uncategorized, "failed to create filesystem volume")) + } + vex_sdk::FRESULT::FR_TIMEOUT => Err(io::Error::new( + io::ErrorKind::TimedOut, + "the function was canceled due to a timeout of thread-safe control", + )), + vex_sdk::FRESULT::FR_LOCKED => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "the operation to the object was rejected by file sharing control", + )), + vex_sdk::FRESULT::FR_NOT_ENOUGH_CORE => { + Err(io::Error::new(io::ErrorKind::OutOfMemory, "not enough memory for the operation")) + } + vex_sdk::FRESULT::FR_TOO_MANY_OPEN_FILES => Err(io::Error::new( + io::ErrorKind::Uncategorized, + "maximum number of open files has been reached", + )), + vex_sdk::FRESULT::FR_INVALID_PARAMETER => { + Err(io::Error::new(io::ErrorKind::InvalidInput, "a given parameter was invalid")) + } + _ => unreachable!(), // C-style enum + } +} diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs new file mode 100644 index 0000000000000..282443046b106 --- /dev/null +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -0,0 +1,123 @@ +#[path = "../unsupported/args.rs"] +pub mod args; +pub mod env; +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/os.rs"] +pub mod os; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +pub mod stdio; +pub mod thread; +pub mod time; + +use crate::arch::global_asm; +use crate::ptr::{self, addr_of_mut}; +use crate::time::{Duration, Instant}; + +global_asm!( + r#" + .section .boot, "ax" + .global _boot + + _boot: + ldr sp, =__stack_top @ Set up the user stack. + b _start @ Jump to the Rust entrypoint. + "# +); + +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + extern "C" { + static mut __bss_start: u8; + static mut __bss_end: u8; + + fn main() -> i32; + } + + // VEXos doesn't explicitly clean out .bss. + ptr::slice_from_raw_parts_mut( + addr_of_mut!(__bss_start), + addr_of_mut!(__bss_end).offset_from(addr_of_mut!(__bss_start)) as usize, + ) + .as_mut() + .unwrap_unchecked() + .fill(0); + + main(); + + abort_internal() +} + +// The code signature is a 32 byte header at the start of user programs that +// identifies the owner and type of the program, as well as certain flags for +// program behavior dictated by the OS. In the event that the user wants to +// change this header, we use weak linkage so it can be overwritten. +#[link_section = ".code_signature"] +#[linkage = "weak"] +#[used] +static CODE_SIGNATURE: vex_sdk::vcodesig = + vex_sdk::vcodesig { magic: u32::from_le_bytes(*b"XVX5"), r#type: 0, owner: 2, options: 0 }; + +// This function is needed by the panic runtime. The symbol is named in +// pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +// NB. used by both libunwind and libpanic_abort +pub extern "C" fn __rust_abort() -> ! { + abort_internal() +} + +// 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 unsupported() -> crate::io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> crate::io::Error { + crate::io::Error::UNSUPPORTED_PLATFORM +} + +pub fn is_interrupted(_code: i32) -> bool { + false +} + +pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { + crate::io::ErrorKind::Uncategorized +} + +pub fn abort_internal() -> ! { + let exit_time = Instant::now(); + const FLUSH_TIMEOUT: Duration = Duration::from_millis(15); + + unsafe { + // Force the serial buffer to flush + while exit_time.elapsed() < FLUSH_TIMEOUT { + vex_sdk::vexTasksRun(); + + // If the buffer has been fully flushed, exit the loop + if vex_sdk::vexSerialWriteFree(stdio::STDIO_CHANNEL) == (stdio::STDOUT_BUF_SIZE as i32) + { + break; + } + } + + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } +} diff --git a/library/std/src/sys/pal/vexos/stdio.rs b/library/std/src/sys/pal/vexos/stdio.rs new file mode 100644 index 0000000000000..94c834843806f --- /dev/null +++ b/library/std/src/sys/pal/vexos/stdio.rs @@ -0,0 +1,108 @@ +use crate::io; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +pub const STDIO_CHANNEL: u32 = 1; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin(()) + } +} + +impl io::Read for Stdin { + fn read(&mut self, mut buf: &mut [u8]) -> io::Result { + let mut written = 0; + + while let Some((out_byte, new_buf)) = buf.split_first_mut() { + buf = new_buf; + + let byte = unsafe { vex_sdk::vexSerialReadChar(STDIO_CHANNEL) }; + if byte < 0 { + break; + } + + *out_byte = byte as u8; + written += 1; + } + + Ok(written) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout(()) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + let written = + unsafe { vex_sdk::vexSerialWriteBuffer(STDIO_CHANNEL, buf.as_ptr(), buf.len() as u32) }; + + if written < 0 { + return Err(io::Error::new( + io::ErrorKind::Uncategorized, + "Internal write error occurred.", + )); + } + + Ok(written as usize) + } + + fn flush(&mut self) -> io::Result<()> { + unsafe { + while (vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize) != STDOUT_BUF_SIZE { + vex_sdk::vexTasksRun(); + } + } + + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + let written = + unsafe { vex_sdk::vexSerialWriteBuffer(STDIO_CHANNEL, buf.as_ptr(), buf.len() as u32) }; + + if written < 0 { + return Err(io::Error::new( + io::ErrorKind::Uncategorized, + "Internal write error occurred.", + )); + } + + Ok(written as usize) + } + + fn flush(&mut self) -> io::Result<()> { + unsafe { + while (vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize) != STDOUT_BUF_SIZE { + vex_sdk::vexTasksRun(); + } + } + + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 4096; +pub const STDOUT_BUF_SIZE: usize = 2048; + +pub fn is_ebadf(_err: &io::Error) -> bool { + false +} + +pub fn panic_output() -> Option { + Some(Stdout::new()) +} diff --git a/library/std/src/sys/pal/vexos/thread.rs b/library/std/src/sys/pal/vexos/thread.rs new file mode 100644 index 0000000000000..f00ff9f7b1066 --- /dev/null +++ b/library/std/src/sys/pal/vexos/thread.rs @@ -0,0 +1,44 @@ +use super::unsupported; +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZero; +use crate::time::{Duration, Instant}; + +pub struct Thread(!); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + unsupported() + } + + pub fn yield_now() { + unsafe { + vex_sdk::vexTasksRun(); + } + } + + pub fn set_name(_name: &CStr) { + // nope + } + + pub fn sleep(dur: Duration) { + let start = Instant::now(); + + while start.elapsed() < dur { + unsafe { + vex_sdk::vexTasksRun(); + } + } + } + + pub fn join(self) { + self.0 + } +} + +pub fn available_parallelism() -> io::Result> { + unsupported() +} diff --git a/library/std/src/sys/pal/vexos/time.rs b/library/std/src/sys/pal/vexos/time.rs new file mode 100644 index 0000000000000..60805ea2759d0 --- /dev/null +++ b/library/std/src/sys/pal/vexos/time.rs @@ -0,0 +1,46 @@ +use crate::time::Duration; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + let micros = unsafe { vex_sdk::vexSystemHighResTimeGet() }; + Self(Duration::from_micros(micros)) + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + panic!("system time not implemented on this platform") + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index f42351deb92c0..a406aef53d1e8 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -75,6 +75,7 @@ cfg_if::cfg_if! { } else if #[cfg(any( all(target_family = "wasm", target_os = "unknown"), target_os = "xous", + target_os = "vexos", ))] { // FIXME: finally remove std support for wasm32-unknown-unknown // FIXME: add random data generation to xous @@ -88,6 +89,7 @@ cfg_if::cfg_if! { target_os = "android", all(target_family = "wasm", target_os = "unknown"), target_os = "xous", + target_os = "vexos", )))] pub fn hashmap_random_keys() -> (u64, u64) { let mut buf = [0; 16]; diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index f0a13323ec93f..30504c0c2b399 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -28,6 +28,7 @@ cfg_if::cfg_if! { all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi", target_os = "zkvm", + target_os = "vexos", ))] { mod statik; pub use statik::{EagerStorage, LazyStorage, thread_local_inner}; @@ -91,6 +92,7 @@ pub(crate) mod guard { )), target_os = "uefi", target_os = "zkvm", + target_os = "vexos", ))] { pub(crate) fn enable() { // FIXME: Right now there is no concept of "thread exit" on diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index c9697e670b777..8728ccd78c0d7 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -85,8 +85,7 @@ dependencies = [ [[package]] name = "cc" version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" +source = "git+https://github.com/rust-lang/cc-rs?rev=6fe995c675dc1178f570752fa8f6de82913a91dc#6fe995c675dc1178f570752fa8f6de82913a91dc" dependencies = [ "shlex", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index d8775a67e1939..8bf90427c568a 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -90,3 +90,7 @@ debug = 0 [profile.dev.package] # Only use debuginfo=1 to further reduce compile times. bootstrap.debug = 1 + +[patch.crates-io] +# FIXME: remove this once the `armv7a-vex-v5` target is available on nightly and cc-rs is updated with it +cc = { git = "https://github.com/rust-lang/cc-rs", rev = "6fe995c675dc1178f570752fa8f6de82913a91dc" } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index ed0155622c226..c8e65bc4c79f6 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -33,6 +33,7 @@ pub struct Finder { // // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ + "armv7a-vex-v5", // just a dummy comment so the list doesn't get onelined ]; diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 8405c22aff08f..8ee9774488cb8 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -641,9 +641,9 @@ impl Build { if self.config.profiler_enabled(target) { features.insert("profiler"); } - - // If zkvm target, generate memcpy, etc. - if target.contains("zkvm") { + // Generate memcpy, etc. FIXME: Remove this once compiler-builtins + // automatically detects this target. + if target.contains("zkvm") || target.contains("vex") { features.insert("compiler-builtins-mem"); } diff --git a/src/doc/book b/src/doc/book index 04d06dfe54160..ad2011d3bcad9 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 04d06dfe541607e6419f3d028c3f9b245f3be4d9 +Subproject commit ad2011d3bcad9f152d034faf7635c22506839d58 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index d56e0f3a0656b..bc4ce51e1d4da 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit d56e0f3a0656b7702ca466d4b191e16c28262b82 +Subproject commit bc4ce51e1d4dacb9350a92e95f6159a42de2f8c6 diff --git a/src/doc/nomicon b/src/doc/nomicon index 7ef05b9777c94..97e84a38c94bf 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 7ef05b9777c94836bc92f50f23e6e00981521a89 +Subproject commit 97e84a38c94bf9362b11284c20b2cb4adaa1e868 diff --git a/src/doc/reference b/src/doc/reference index acd6794e712d5..9f41bc11342d4 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit acd6794e712d5e2ef6f5c84fb95688d32a69b816 +Subproject commit 9f41bc11342d46544ae0732caf14ec0bcaf27376 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 093397535b48a..76406337f4131 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 093397535b48ae13ec76bc526b7e6eb8c096a85c +Subproject commit 76406337f4131253443aea0ed7e7f451b464117c diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 01fabfb39a77a..b1cdc08954bf2 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -45,6 +45,7 @@ - [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md) - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md) - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md) + - [armv7a-vex-v5](platform-support/armv7a-vex-v5.md) - [\*-android and \*-androideabi](platform-support/android.md) - [\*-linux-ohos](platform-support/openharmony.md) - [\*-hurd-gnu](platform-support/hurd.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 1d202402288c8..c0f483d359b77 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -160,6 +160,7 @@ target | std | notes `armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | Armv7-A OpenHarmony [`armv7a-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare Armv7-A +[`armv7a-vex-v5`](platform-support/armv7a-vex-v5.md) | * | Armv7-A Cortex-A9 VEX V5 Brain, VEXos [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat `i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE [^x86_32-floats-x87] diff --git a/src/doc/rustc/src/platform-support/armv7a-vex-v5.md b/src/doc/rustc/src/platform-support/armv7a-vex-v5.md new file mode 100644 index 0000000000000..1cd3e889bd889 --- /dev/null +++ b/src/doc/rustc/src/platform-support/armv7a-vex-v5.md @@ -0,0 +1,107 @@ +# `armv7a-vex-v5` + +**Tier: 3** + +Allows compiling user programs for the [VEX V5 Brain](https://www.vexrobotics.com/276-4810.html), a microcontroller for educational and competitive robotics. + +Rust support for this target is not affiliated with VEX Robotics or IFI. + +## Target maintainers + +This target is maintained by members of the [vexide](https://github.com/vexide) organization: + +- [@max-niederman](https://github.com/max-niederman) +- [@Tropix126](https://github.com/Tropix126) +- [@Gavin-Niederman](https://github.com/Gavin-Niederman) +- [@doinkythederp](https://github.com/doinkythederp) + +## Requirements + +This target is cross-compiled. Dynamic linking is unsupported. + +`#![no_std]` crates can be built using `build-std` to build `core` and optionally +`alloc`. Unwinding panics are not yet supported. `std` is partially implemented, see below. + +This target generates binaries in the ELF format that may uploaded to the brain with external tools. + +## Standard Library Support + +`armv7a-vex-v5` implements as much of the standard library as is possible using only public VEX SDK functions. +This includes: + +- `std::time`, not including `SystemTime` as the SDK does not provide absolute time information. +- `std::io`, including `stdin`/`stdout`/`stderr`. `stdout` and `stderr` are both written to USB channel 1 on this platform and are not differentiated. +- `std::fs`, with the exception of directory reading and file deletion, due to public SDK limitations. +- modules which do not need to interact with the OS beyond allocation, + such as `std::collections`, `std::hash`, `std::future`, `std::sync`, etc. + +Notable modules which are not implemented include: + +- `std::process` +- `std::thread` +- `std::net` + +## Building the target + +Rust does not ship pre-compiled artifacts for this target. You can use the `build-std` feature to build ELF binaries with `std` support. + +`.cargo/config.toml`: + +```toml +[build] +target = "armv7a-vex-v5" + +[unstable] +build-std = ["std", "panic_abort"] +build-std-features = ["compiler-builtins-mem"] +``` + +## Building Rust programs + +When the compiler builds a binary, an ELF build artifact will be produced. Additional tools are required for this artifact to be recognizable to VEXos as a user program. + +The [cargo-v5](https://github.com/vexide/cargo-v5) tool is capable of creating binaries that can be uploaded to the V5 brain. This tool wraps the `cargo build` command by supplying arguments necessary to build the target and produce an artifact recognizable to VEXos, while also providing functionality for uploading over USB to a V5 Controller or Brain. + +To install the tool, run: + +```sh +cargo install cargo-v5 +``` + +The following fields in your project's `Cargo.toml` are read by `cargo-v5` to configure upload behavior: + +```toml +[package.metadata.v5] +# Slot number to upload the user program to. This should be from 1-8. +slot = 1 +# Program icon/thumbnail that will be displayed on the dashboard. +icon = "cool-x" +# Use gzip compression when uploading binaries. +compress = true +``` + +To build an uploadable BIN file using the release profile, run: + +```sh +cargo v5 build --release +``` + +Programs can also be directly uploaded to the brain over a USB connection immediately after building: + +```sh +cargo v5 upload --release +``` + +## Testing + +Binaries built for this target can be run in an emulator (such as [vex-v5-qemu](https://github.com/vexide/vex-v5-qemu)), or uploaded to a physical device over a serial (USB) connection. + +The default Rust test runner is not supported. + +The Rust test suite for `library/std` is not yet supported. + +## Cross-compilation toolchains and C code + +This target can be cross-compiled from any host. + +This target does not link to C libraries. OS calls are implemented in rust through the [vex-sdk](https://github.com/vexide/vex-sdk) crate. No `libc` or crt0 implementation is present on this target. diff --git a/src/tools/cargo b/src/tools/cargo index fd784878cfa84..c86f4b3a1b153 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit fd784878cfa843e3e29a6654ecf564c62fae6735 +Subproject commit c86f4b3a1b153218e6e50861214b0b4b4e695f23 diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index b71ce92771ae1..33069cddfdb8b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -487,6 +487,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ "shlex", "unicode-width", "unwinding", + "vex-sdk", "wasi", "windows-sys", "windows-targets", diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 55f937aeacf50..be19807c7e6a2 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -8,6 +8,8 @@ const ALLOWED_SOURCES: &[&str] = &[ r#""registry+https://github.com/rust-lang/crates.io-index""#, // This is `rust_team_data` used by `site` in src/tools/rustc-perf, r#""git+https://github.com/rust-lang/team#a5260e76d3aa894c64c56e6ddc8545b9a98043ec""#, + // This is temporarily used to bootstrap the `armv7a_vex_v5` target until cc-rs has it in its generated target list. + r#""git+https://github.com/rust-lang/cc-rs?rev=6fe995c675dc1178f570752fa8f6de82913a91dc#6fe995c675dc1178f570752fa8f6de82913a91dc""#, ]; /// Checks for external package sources. `root` is the path to the directory that contains the diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index 69b42de9e1020..c3bdeb1dc7bc9 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -177,6 +177,9 @@ //@ revisions: armv7a_none_eabihf //@ [armv7a_none_eabihf] compile-flags: --target armv7a-none-eabihf //@ [armv7a_none_eabihf] needs-llvm-components: arm +//@ revisions: armv7a_vex_v5 +//@ [armv7a_vex_v5] compile-flags: --target armv7a-vex-v5 +//@ [armv7a_vex_v5] needs-llvm-components: arm //@ revisions: armv7r_none_eabi //@ [armv7r_none_eabi] compile-flags: --target armv7r-none-eabi //@ [armv7r_none_eabi] needs-llvm-components: arm diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr index 23b6edacce77c..36420cc165074 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `vex`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `feature` diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 804d7fb9163d7..e9e9f540be802 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `vex`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 804d7fb9163d7..e9e9f540be802 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `vex`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 5c1898a0ae365..82d8c1a20b049 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, and `uclibc` + = note: expected values for `target_env` are: ``, `gnu`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, `uclibc`, and `v5` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -201,7 +201,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -230,7 +230,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_vendor = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `vex`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -274,7 +274,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `vexos`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: 28 warnings emitted