diff --git a/build-fibonacci.sh b/build-fibonacci.sh index 2ef9558..b21aed2 100755 --- a/build-fibonacci.sh +++ b/build-fibonacci.sh @@ -13,7 +13,7 @@ TARGET_TRIPLE="riscv64imac-unknown-none-elf" OUT_DIR="${ROOT}/target/${TARGET_TRIPLE}/$([ "$PROFILE" = "dev" ] && echo debug || echo "$PROFILE")" BIN="${OUT_DIR}/fibonacci" -cargo spike build -p fibonacci --target "${TARGET_TRIPLE}" -- --quiet --features=debug --profile "${PROFILE}" +cargo spike build -p fibonacci --target "${TARGET_TRIPLE}" -- --quiet --features=debug,with-spike --profile "${PROFILE}" OUT_NOSTD="$(mktemp)" OUT_STD="$(mktemp)" trap 'rm -f "${OUT_NOSTD}" "${OUT_STD}"' EXIT @@ -28,7 +28,7 @@ TARGET_TRIPLE="riscv64imac-zero-linux-musl" OUT_DIR="${ROOT}/target/${TARGET_TRIPLE}/$([ "$PROFILE" = "dev" ] && echo debug || echo "$PROFILE")" BIN="${OUT_DIR}/fibonacci" -cargo spike build -p fibonacci --target "${TARGET_TRIPLE}" --mode std -- --quiet --features=std,debug --profile "${PROFILE}" +cargo spike build -p fibonacci --target "${TARGET_TRIPLE}" --mode std -- --quiet --features=std,debug,with-spike --profile "${PROFILE}" RUST_LOG=debug cargo spike run "${BIN}" --isa RV64IMAC --instructions 100000000 | tee "${OUT_STD}" grep -q "fibonacci(10) = 55" "${OUT_STD}" grep -q "Test PASSED" "${OUT_STD}" diff --git a/build-std-smoke.sh b/build-std-smoke.sh index e9105ff..d5d80bb 100755 --- a/build-std-smoke.sh +++ b/build-std-smoke.sh @@ -11,7 +11,7 @@ BIN="${OUT_DIR}/std-smoke" cd "${ROOT}" echo "Building std-smoke example..." -cargo spike build -p std-smoke --target "${TARGET_TRIPLE}" --mode std --backtrace=enable -- --quiet --features=std,backtrace --profile "${PROFILE}" +cargo spike build -p std-smoke --target "${TARGET_TRIPLE}" --mode std --backtrace=enable --memory-size=40MiB --stack-size=4MiB --heap-size=8MiB -- --features=std,backtrace,with-spike --profile "${PROFILE}" echo "Running on Spike simulator..." OUT="$(mktemp)" diff --git a/crates/zeroos-arch-riscv/build.rs b/crates/zeroos-arch-riscv/build.rs index 5f97123..3f33b9f 100644 --- a/crates/zeroos-arch-riscv/build.rs +++ b/crates/zeroos-arch-riscv/build.rs @@ -2,8 +2,9 @@ fn main() { let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default(); if arch != "riscv32" && arch != "riscv64" { panic!( - "{} is RISC-V-only; build with a RISC-V target.", - env!("CARGO_PKG_NAME") + "{} is RISC-V-only; build with a RISC-V target (current: {}).", + env!("CARGO_PKG_NAME"), + arch ); } } diff --git a/crates/zeroos-arch-riscv/src/trap.rs b/crates/zeroos-arch-riscv/src/trap.rs index f477575..fb786d2 100644 --- a/crates/zeroos-arch-riscv/src/trap.rs +++ b/crates/zeroos-arch-riscv/src/trap.rs @@ -1,4 +1,6 @@ //! Platforms must provide `trap_handler(regs: *mut TrapFrame)`; this crate provides the entry/exit wrapper. +#[cfg(not(target_os = "none"))] +use core::arch::global_asm; use cfg_if::cfg_if; @@ -153,8 +155,6 @@ cfg_if! { } } -use core::arch::global_asm; - mod imp { use super::*; @@ -305,6 +305,7 @@ mod imp { pub use imp::_default_trap_handler; +#[cfg(not(target_os = "none"))] global_asm!( ".align 2", ".weak _trap_handler", diff --git a/crates/zeroos-build/src/cmds/build.rs b/crates/zeroos-build/src/cmds/build.rs index c51133c..01d5a44 100644 --- a/crates/zeroos-build/src/cmds/build.rs +++ b/crates/zeroos-build/src/cmds/build.rs @@ -72,6 +72,17 @@ pub fn build_binary( args: &BuildArgs, toolchain_paths: Option<(PathBuf, PathBuf)>, linker_template: Option, +) -> Result<()> { + build_binary_with_rustflags(workspace_root, args, toolchain_paths, linker_template, None) +} + +/// Build binary with optional additional rustflags (for platform-specific flags) +pub fn build_binary_with_rustflags( + workspace_root: &PathBuf, + args: &BuildArgs, + toolchain_paths: Option<(PathBuf, PathBuf)>, + linker_template: Option, + additional_rustflags: Option<&[&str]>, ) -> Result<()> { info!( "Building binary for {:?} mode (fully: {})", @@ -195,6 +206,13 @@ pub fn build_binary( rustflags_parts.push("-Zmacro-backtrace".to_string()); } + // Add platform-specific rustflags + if let Some(flags) = additional_rustflags { + for flag in flags { + rustflags_parts.push(flag.to_string()); + } + } + let encoded_rustflags = rustflags_parts.join("\x1f"); debug!("CARGO_ENCODED_RUSTFLAGS: {:?}", encoded_rustflags); diff --git a/crates/zeroos-build/src/cmds/mod.rs b/crates/zeroos-build/src/cmds/mod.rs index 6c48d06..35e2171 100644 --- a/crates/zeroos-build/src/cmds/mod.rs +++ b/crates/zeroos-build/src/cmds/mod.rs @@ -3,7 +3,8 @@ pub mod linker; pub mod target; pub use build::{ - build_binary, find_workspace_root, get_or_build_toolchain, parse_address, BuildArgs, StdMode, + build_binary, build_binary_with_rustflags, find_workspace_root, get_or_build_toolchain, + parse_address, BuildArgs, StdMode, }; pub use linker::{generate_linker_script, GenerateLinkerArgs, LinkerGeneratorResult}; pub use target::{generate_target_spec, GenerateTargetArgs}; diff --git a/crates/zeroos-build/src/toolchain/install.rs b/crates/zeroos-build/src/toolchain/install.rs index 4f58900..87e84d3 100644 --- a/crates/zeroos-build/src/toolchain/install.rs +++ b/crates/zeroos-build/src/toolchain/install.rs @@ -63,40 +63,6 @@ fn host_arch() -> &'static str { } } -fn default_repo_from_git() -> Option { - let out = Command::new("git") - .args(["config", "--get", "remote.origin.url"]) - .output() - .ok()?; - if !out.status.success() { - return None; - } - let url = String::from_utf8_lossy(&out.stdout).trim().to_string(); - if url.is_empty() { - return None; - } - - // https://github.com/ORG/REPO.git - if let Some(rest) = url.strip_prefix("https://github.com/") { - let rest = rest.strip_suffix(".git").unwrap_or(rest); - let mut parts = rest.split('/'); - let owner = parts.next()?; - let repo = parts.next()?; - return Some(format!("{}/{}", owner, repo)); - } - - // git@github.com:ORG/REPO.git - if let Some(rest) = url.strip_prefix("git@github.com:") { - let rest = rest.strip_suffix(".git").unwrap_or(rest); - let mut parts = rest.split('/'); - let owner = parts.next()?; - let repo = parts.next()?; - return Some(format!("{}/{}", owner, repo)); - } - - None -} - fn run(cmd: &mut Command) -> Result<(), String> { debug!("Running command: {:?}", cmd); let status = cmd @@ -214,7 +180,6 @@ pub fn install_musl_toolchain(config: &InstallConfig) -> Result install_musl_toolchain(install), } } + +/// Resolve toolchain, or download pre-built, or build from source as last resort. +/// +/// Fallback chain: +/// 1. Try to resolve existing toolchain from args/env/default locations +/// 2. Try to download pre-built toolchain from GitHub Releases +/// 3. If `build_fallback` is true, build from source as last resort +pub fn get_or_install_or_build_toolchain( + musl_lib_arg: Option, + gcc_lib_arg: Option, + config: &ToolchainConfig, + install: &InstallConfig, + build_fallback: bool, +) -> Result { + use super::{build_musl_toolchain, resolve_toolchain_paths, BuildConfig}; + + // Try existing paths + if let Ok(paths) = resolve_toolchain_paths(musl_lib_arg, gcc_lib_arg, config) { + return Ok(paths); + } + + // Try downloading pre-built + match install_musl_toolchain(install) { + Ok(paths) => return Ok(paths), + Err(e) if build_fallback => { + info!("Download failed: {}. Building from source...", e); + } + Err(e) => return Err(e), + } + + // Build from source + info!("Building toolchain from source (this may take 5-15 minutes)..."); + let build_config = BuildConfig { + arch: config.arch.clone(), + output_dir: install.output_dir.clone(), + ..BuildConfig::default() + }; + build_musl_toolchain(&build_config) +} diff --git a/crates/zeroos-build/src/toolchain/mod.rs b/crates/zeroos-build/src/toolchain/mod.rs index 2c433e7..9b037af 100644 --- a/crates/zeroos-build/src/toolchain/mod.rs +++ b/crates/zeroos-build/src/toolchain/mod.rs @@ -4,7 +4,10 @@ mod discovery; mod install; pub use discovery::{discover_toolchain, validate_toolchain_path, ToolchainPaths}; -pub use install::{get_or_install_toolchain, install_musl_toolchain, InstallConfig}; +pub use install::{ + get_or_install_or_build_toolchain, get_or_install_toolchain, install_musl_toolchain, + InstallConfig, +}; use std::format; use std::path::{Path, PathBuf}; diff --git a/crates/zeroos-foundation/src/entry.rs b/crates/zeroos-foundation/src/entry.rs index bd369a7..bce2501 100644 --- a/crates/zeroos-foundation/src/entry.rs +++ b/crates/zeroos-foundation/src/entry.rs @@ -1,4 +1,19 @@ -// - `#[no_mangle] fn main() -> !` (Rust, must call exit()) +// Entry point for calling the user's main() function. +// +// `__main_entry` is defined as a weak symbol that jumps to `__default_main_entry`. +// Platforms/SDKs can provide their own strong `__main_entry` to override this. + +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +use core::arch::global_asm; + +unsafe extern "C" { + /// # Safety + /// Entry point for the user's main function. + /// This is a weak symbol defined by foundation that can be overridden + /// by platforms/SDKs that need a different main() signature. + pub unsafe fn __main_entry(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; +} + cfg_if::cfg_if! { if #[cfg(feature = "libc-main")] { extern "C" { @@ -10,7 +25,7 @@ cfg_if::cfg_if! { /// # Safety /// The caller must provide `argv` and `envp` pointers that are valid per the platform ABI /// (or null), and remain valid for the duration of the call. - pub unsafe extern "C" fn __main_entry(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32 { + pub unsafe extern "C" fn __default_main_entry(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32 { main(argc, argv, envp) } } else { @@ -22,7 +37,7 @@ cfg_if::cfg_if! { #[no_mangle] #[inline(never)] - pub extern "C" fn __main_entry(_argc: i32, _argv: *const *const u8, _envp: *const *const u8) -> i32 { + pub extern "C" fn __default_main_entry(_argc: i32, _argv: *const *const u8, _envp: *const *const u8) -> i32 { debug::writeln!("[BOOT] __main_entry argc={} argv=0x{:x}", _argc, _argv as usize); unsafe { @@ -32,3 +47,14 @@ cfg_if::cfg_if! { } } } + +// Define __main_entry as a weak symbol that jumps to __default_main_entry. +// Platforms providing their own __main_entry can define a strong symbol to override. +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +global_asm!( + ".weak __main_entry", + ".type __main_entry, @function", + "__main_entry:", + "j {default}", + default = sym __default_main_entry, +); diff --git a/crates/zeroos-os-linux/src/syscall.rs b/crates/zeroos-os-linux/src/syscall.rs index 1c1755d..d746ce7 100644 --- a/crates/zeroos-os-linux/src/syscall.rs +++ b/crates/zeroos-os-linux/src/syscall.rs @@ -181,6 +181,392 @@ sys_registry! { } } +/// Returns the name of a syscall given its number. +/// +/// Only includes syscalls available on riscv64 (the primary target for zeroos-os-linux). +#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] +#[allow(non_snake_case)] +pub fn syscall_name(nr: usize) -> &'static str { + match nr as i64 { + // Process control + SYS_exit => "SYS_exit", + SYS_exit_group => "SYS_exit_group", + SYS_clone => "SYS_clone", + SYS_clone3 => "SYS_clone3", + SYS_execve => "SYS_execve", + SYS_execveat => "SYS_execveat", + SYS_wait4 => "SYS_wait4", + SYS_waitid => "SYS_waitid", + SYS_kill => "SYS_kill", + SYS_tkill => "SYS_tkill", + SYS_tgkill => "SYS_tgkill", + SYS_getpid => "SYS_getpid", + SYS_getppid => "SYS_getppid", + SYS_gettid => "SYS_gettid", + SYS_set_tid_address => "SYS_set_tid_address", + SYS_prctl => "SYS_prctl", + SYS_ptrace => "SYS_ptrace", + + // Thread/futex + SYS_futex => "SYS_futex", + SYS_get_robust_list => "SYS_get_robust_list", + SYS_set_robust_list => "SYS_set_robust_list", + + // Scheduling + SYS_sched_yield => "SYS_sched_yield", + SYS_sched_getaffinity => "SYS_sched_getaffinity", + SYS_sched_setaffinity => "SYS_sched_setaffinity", + SYS_sched_getparam => "SYS_sched_getparam", + SYS_sched_setparam => "SYS_sched_setparam", + SYS_sched_getscheduler => "SYS_sched_getscheduler", + SYS_sched_setscheduler => "SYS_sched_setscheduler", + SYS_sched_get_priority_max => "SYS_sched_get_priority_max", + SYS_sched_get_priority_min => "SYS_sched_get_priority_min", + SYS_sched_rr_get_interval => "SYS_sched_rr_get_interval", + + // Memory management + SYS_brk => "SYS_brk", + SYS_mmap => "SYS_mmap", + SYS_munmap => "SYS_munmap", + SYS_mprotect => "SYS_mprotect", + SYS_mremap => "SYS_mremap", + SYS_madvise => "SYS_madvise", + SYS_mlock => "SYS_mlock", + SYS_mlock2 => "SYS_mlock2", + SYS_munlock => "SYS_munlock", + SYS_mlockall => "SYS_mlockall", + SYS_munlockall => "SYS_munlockall", + SYS_msync => "SYS_msync", + SYS_mincore => "SYS_mincore", + SYS_membarrier => "SYS_membarrier", + + // File operations + SYS_openat => "SYS_openat", + SYS_openat2 => "SYS_openat2", + SYS_close => "SYS_close", + SYS_close_range => "SYS_close_range", + SYS_read => "SYS_read", + SYS_write => "SYS_write", + SYS_readv => "SYS_readv", + SYS_writev => "SYS_writev", + SYS_pread64 => "SYS_pread64", + SYS_pwrite64 => "SYS_pwrite64", + SYS_preadv => "SYS_preadv", + SYS_pwritev => "SYS_pwritev", + SYS_preadv2 => "SYS_preadv2", + SYS_pwritev2 => "SYS_pwritev2", + SYS_lseek => "SYS_lseek", + SYS_ioctl => "SYS_ioctl", + SYS_fcntl => "SYS_fcntl", + SYS_dup => "SYS_dup", + SYS_dup3 => "SYS_dup3", + SYS_flock => "SYS_flock", + SYS_fsync => "SYS_fsync", + SYS_fdatasync => "SYS_fdatasync", + SYS_truncate => "SYS_truncate", + SYS_ftruncate => "SYS_ftruncate", + SYS_fallocate => "SYS_fallocate", + SYS_fadvise64 => "SYS_fadvise64", + SYS_readahead => "SYS_readahead", + SYS_sendfile => "SYS_sendfile", + SYS_splice => "SYS_splice", + SYS_tee => "SYS_tee", + SYS_sync_file_range => "SYS_sync_file_range", + SYS_vmsplice => "SYS_vmsplice", + SYS_copy_file_range => "SYS_copy_file_range", + + // File stat/metadata + SYS_fstat => "SYS_fstat", + SYS_newfstatat => "SYS_newfstatat", + SYS_statx => "SYS_statx", + SYS_faccessat => "SYS_faccessat", + SYS_faccessat2 => "SYS_faccessat2", + SYS_fchmod => "SYS_fchmod", + SYS_fchmodat => "SYS_fchmodat", + SYS_fchown => "SYS_fchown", + SYS_fchownat => "SYS_fchownat", + SYS_utimensat => "SYS_utimensat", + + // Directory operations + SYS_getdents64 => "SYS_getdents64", + SYS_getcwd => "SYS_getcwd", + SYS_chdir => "SYS_chdir", + SYS_fchdir => "SYS_fchdir", + SYS_mkdirat => "SYS_mkdirat", + SYS_mknodat => "SYS_mknodat", + SYS_unlinkat => "SYS_unlinkat", + SYS_renameat2 => "SYS_renameat2", + SYS_linkat => "SYS_linkat", + SYS_symlinkat => "SYS_symlinkat", + SYS_readlinkat => "SYS_readlinkat", + SYS_pivot_root => "SYS_pivot_root", + SYS_mount => "SYS_mount", + SYS_umount2 => "SYS_umount2", + SYS_chroot => "SYS_chroot", + + // File descriptors / poll + SYS_ppoll => "SYS_ppoll", + SYS_pselect6 => "SYS_pselect6", + SYS_epoll_create1 => "SYS_epoll_create1", + SYS_epoll_ctl => "SYS_epoll_ctl", + SYS_epoll_pwait => "SYS_epoll_pwait", + SYS_epoll_pwait2 => "SYS_epoll_pwait2", + SYS_eventfd2 => "SYS_eventfd2", + SYS_signalfd4 => "SYS_signalfd4", + SYS_timerfd_create => "SYS_timerfd_create", + SYS_timerfd_settime => "SYS_timerfd_settime", + SYS_timerfd_gettime => "SYS_timerfd_gettime", + SYS_inotify_init1 => "SYS_inotify_init1", + SYS_inotify_add_watch => "SYS_inotify_add_watch", + SYS_inotify_rm_watch => "SYS_inotify_rm_watch", + SYS_fanotify_init => "SYS_fanotify_init", + SYS_fanotify_mark => "SYS_fanotify_mark", + + // Pipes + SYS_pipe2 => "SYS_pipe2", + + // Sockets + SYS_socket => "SYS_socket", + SYS_socketpair => "SYS_socketpair", + SYS_bind => "SYS_bind", + SYS_listen => "SYS_listen", + SYS_accept => "SYS_accept", + SYS_accept4 => "SYS_accept4", + SYS_connect => "SYS_connect", + SYS_getsockname => "SYS_getsockname", + SYS_getpeername => "SYS_getpeername", + SYS_sendto => "SYS_sendto", + SYS_recvfrom => "SYS_recvfrom", + SYS_sendmsg => "SYS_sendmsg", + SYS_recvmsg => "SYS_recvmsg", + SYS_sendmmsg => "SYS_sendmmsg", + SYS_recvmmsg => "SYS_recvmmsg", + SYS_shutdown => "SYS_shutdown", + SYS_setsockopt => "SYS_setsockopt", + SYS_getsockopt => "SYS_getsockopt", + + // Signals + SYS_rt_sigaction => "SYS_rt_sigaction", + SYS_rt_sigprocmask => "SYS_rt_sigprocmask", + SYS_rt_sigreturn => "SYS_rt_sigreturn", + SYS_rt_sigsuspend => "SYS_rt_sigsuspend", + SYS_rt_sigpending => "SYS_rt_sigpending", + SYS_rt_sigtimedwait => "SYS_rt_sigtimedwait", + SYS_rt_sigqueueinfo => "SYS_rt_sigqueueinfo", + SYS_rt_tgsigqueueinfo => "SYS_rt_tgsigqueueinfo", + SYS_sigaltstack => "SYS_sigaltstack", + + // Time + SYS_clock_gettime => "SYS_clock_gettime", + SYS_clock_settime => "SYS_clock_settime", + SYS_clock_getres => "SYS_clock_getres", + SYS_clock_nanosleep => "SYS_clock_nanosleep", + SYS_clock_adjtime => "SYS_clock_adjtime", + SYS_gettimeofday => "SYS_gettimeofday", + SYS_settimeofday => "SYS_settimeofday", + SYS_adjtimex => "SYS_adjtimex", + SYS_nanosleep => "SYS_nanosleep", + SYS_getitimer => "SYS_getitimer", + SYS_setitimer => "SYS_setitimer", + SYS_times => "SYS_times", + SYS_timer_create => "SYS_timer_create", + SYS_timer_settime => "SYS_timer_settime", + SYS_timer_gettime => "SYS_timer_gettime", + SYS_timer_getoverrun => "SYS_timer_getoverrun", + SYS_timer_delete => "SYS_timer_delete", + + // User/group IDs + SYS_getuid => "SYS_getuid", + SYS_geteuid => "SYS_geteuid", + SYS_getgid => "SYS_getgid", + SYS_getegid => "SYS_getegid", + SYS_setuid => "SYS_setuid", + SYS_setgid => "SYS_setgid", + SYS_setreuid => "SYS_setreuid", + SYS_setregid => "SYS_setregid", + SYS_setresuid => "SYS_setresuid", + SYS_setresgid => "SYS_setresgid", + SYS_getresuid => "SYS_getresuid", + SYS_getresgid => "SYS_getresgid", + SYS_setfsuid => "SYS_setfsuid", + SYS_setfsgid => "SYS_setfsgid", + SYS_getgroups => "SYS_getgroups", + SYS_setgroups => "SYS_setgroups", + + // Session/process group + SYS_setsid => "SYS_setsid", + SYS_getsid => "SYS_getsid", + SYS_setpgid => "SYS_setpgid", + SYS_getpgid => "SYS_getpgid", + SYS_vhangup => "SYS_vhangup", + + // Resource limits + SYS_getrlimit => "SYS_getrlimit", + SYS_setrlimit => "SYS_setrlimit", + SYS_prlimit64 => "SYS_prlimit64", + SYS_getrusage => "SYS_getrusage", + SYS_getpriority => "SYS_getpriority", + SYS_setpriority => "SYS_setpriority", + + // System info + SYS_uname => "SYS_uname", + SYS_sysinfo => "SYS_sysinfo", + SYS_syslog => "SYS_syslog", + SYS_getrandom => "SYS_getrandom", + SYS_getcpu => "SYS_getcpu", + + // Capabilities + SYS_capget => "SYS_capget", + SYS_capset => "SYS_capset", + + // Misc + SYS_umask => "SYS_umask", + SYS_personality => "SYS_personality", + SYS_reboot => "SYS_reboot", + SYS_sync => "SYS_sync", + SYS_syncfs => "SYS_syncfs", + SYS_statfs => "SYS_statfs", + SYS_fstatfs => "SYS_fstatfs", + SYS_swapon => "SYS_swapon", + SYS_swapoff => "SYS_swapoff", + SYS_sethostname => "SYS_sethostname", + SYS_setdomainname => "SYS_setdomainname", + SYS_acct => "SYS_acct", + SYS_quotactl => "SYS_quotactl", + SYS_nfsservctl => "SYS_nfsservctl", + SYS_lookup_dcookie => "SYS_lookup_dcookie", + SYS_remap_file_pages => "SYS_remap_file_pages", + SYS_restart_syscall => "SYS_restart_syscall", + + // Modules + SYS_init_module => "SYS_init_module", + SYS_delete_module => "SYS_delete_module", + SYS_finit_module => "SYS_finit_module", + SYS_kexec_load => "SYS_kexec_load", + + // io_uring + SYS_io_uring_setup => "SYS_io_uring_setup", + SYS_io_uring_enter => "SYS_io_uring_enter", + SYS_io_uring_register => "SYS_io_uring_register", + + // Async I/O (legacy) + SYS_io_setup => "SYS_io_setup", + SYS_io_destroy => "SYS_io_destroy", + SYS_io_submit => "SYS_io_submit", + SYS_io_cancel => "SYS_io_cancel", + SYS_io_getevents => "SYS_io_getevents", + SYS_io_pgetevents => "SYS_io_pgetevents", + + // Shared memory + SYS_shmget => "SYS_shmget", + SYS_shmat => "SYS_shmat", + SYS_shmdt => "SYS_shmdt", + SYS_shmctl => "SYS_shmctl", + + // Semaphores + SYS_semget => "SYS_semget", + SYS_semop => "SYS_semop", + SYS_semtimedop => "SYS_semtimedop", + SYS_semctl => "SYS_semctl", + + // Message queues + SYS_msgget => "SYS_msgget", + SYS_msgsnd => "SYS_msgsnd", + SYS_msgrcv => "SYS_msgrcv", + SYS_msgctl => "SYS_msgctl", + SYS_mq_open => "SYS_mq_open", + SYS_mq_unlink => "SYS_mq_unlink", + SYS_mq_timedsend => "SYS_mq_timedsend", + SYS_mq_timedreceive => "SYS_mq_timedreceive", + SYS_mq_notify => "SYS_mq_notify", + SYS_mq_getsetattr => "SYS_mq_getsetattr", + + // Keys + SYS_add_key => "SYS_add_key", + SYS_request_key => "SYS_request_key", + SYS_keyctl => "SYS_keyctl", + + // Extended attributes + SYS_setxattr => "SYS_setxattr", + SYS_lsetxattr => "SYS_lsetxattr", + SYS_fsetxattr => "SYS_fsetxattr", + SYS_getxattr => "SYS_getxattr", + SYS_lgetxattr => "SYS_lgetxattr", + SYS_fgetxattr => "SYS_fgetxattr", + SYS_listxattr => "SYS_listxattr", + SYS_llistxattr => "SYS_llistxattr", + SYS_flistxattr => "SYS_flistxattr", + SYS_removexattr => "SYS_removexattr", + SYS_lremovexattr => "SYS_lremovexattr", + SYS_fremovexattr => "SYS_fremovexattr", + + // Namespaces + SYS_setns => "SYS_setns", + SYS_unshare => "SYS_unshare", + + // NUMA + SYS_mbind => "SYS_mbind", + SYS_set_mempolicy => "SYS_set_mempolicy", + SYS_get_mempolicy => "SYS_get_mempolicy", + SYS_migrate_pages => "SYS_migrate_pages", + SYS_move_pages => "SYS_move_pages", + + // Priority I/O + SYS_ioprio_set => "SYS_ioprio_set", + SYS_ioprio_get => "SYS_ioprio_get", + + // Process VM + SYS_process_vm_readv => "SYS_process_vm_readv", + SYS_process_vm_writev => "SYS_process_vm_writev", + SYS_process_madvise => "SYS_process_madvise", + SYS_kcmp => "SYS_kcmp", + + // Scheduling (newer) + SYS_sched_setattr => "SYS_sched_setattr", + SYS_sched_getattr => "SYS_sched_getattr", + + // Memory (newer) + SYS_memfd_create => "SYS_memfd_create", + SYS_pkey_mprotect => "SYS_pkey_mprotect", + SYS_pkey_alloc => "SYS_pkey_alloc", + SYS_pkey_free => "SYS_pkey_free", + SYS_rseq => "SYS_rseq", + + // Mount (newer) + SYS_open_tree => "SYS_open_tree", + SYS_move_mount => "SYS_move_mount", + SYS_fsopen => "SYS_fsopen", + SYS_fsconfig => "SYS_fsconfig", + SYS_fsmount => "SYS_fsmount", + SYS_fspick => "SYS_fspick", + SYS_mount_setattr => "SYS_mount_setattr", + SYS_name_to_handle_at => "SYS_name_to_handle_at", + SYS_open_by_handle_at => "SYS_open_by_handle_at", + + // Security + SYS_seccomp => "SYS_seccomp", + SYS_bpf => "SYS_bpf", + SYS_landlock_create_ruleset => "SYS_landlock_create_ruleset", + SYS_landlock_add_rule => "SYS_landlock_add_rule", + SYS_landlock_restrict_self => "SYS_landlock_restrict_self", + + // Misc newer syscalls + SYS_userfaultfd => "SYS_userfaultfd", + SYS_perf_event_open => "SYS_perf_event_open", + SYS_pidfd_open => "SYS_pidfd_open", + SYS_pidfd_send_signal => "SYS_pidfd_send_signal", + SYS_pidfd_getfd => "SYS_pidfd_getfd", + + _ => "SYS_unknown", + } +} + +/// Fallback for non-riscv targets (for cross-compilation). +#[cfg(not(any(target_arch = "riscv64", target_arch = "riscv32")))] +pub fn syscall_name(_nr: usize) -> &'static str { + "SYS_unknown" +} + /// # Safety /// `regs` must be a valid pointer to a syscall frame. pub unsafe fn dispatch_syscall(regs: *mut Frame) { diff --git a/crates/zeroos-runtime-musl/src/eh_frame_register.rs b/crates/zeroos-runtime-musl/src/eh_frame_register.rs index 94191f5..1842d2d 100644 --- a/crates/zeroos-runtime-musl/src/eh_frame_register.rs +++ b/crates/zeroos-runtime-musl/src/eh_frame_register.rs @@ -2,8 +2,6 @@ //! //! We do this from `.init_array` so it runs after musl's `__init_libc` (malloc/env are ready) //! but before `main`, avoiding early-boot allocations/faults. -#![cfg(feature = "backtrace")] - extern "C" { // libgcc frame registration API (DWARF2 unwinder) fn __register_frame(begin: *const u8); diff --git a/crates/zeroos-runtime-musl/src/riscv64/mod.rs b/crates/zeroos-runtime-musl/src/riscv64/mod.rs index f3c416c..267b180 100644 --- a/crates/zeroos-runtime-musl/src/riscv64/mod.rs +++ b/crates/zeroos-runtime-musl/src/riscv64/mod.rs @@ -1,3 +1 @@ mod bootstrap; - -pub use bootstrap::{__runtime_bootstrap, _fini, _init}; diff --git a/crates/zeroos/src/lib.rs b/crates/zeroos/src/lib.rs index 9cebe4d..0ac2be7 100644 --- a/crates/zeroos/src/lib.rs +++ b/crates/zeroos/src/lib.rs @@ -7,6 +7,9 @@ pub use foundation; pub use zeroos_macros as macros; +#[cfg(feature = "debug")] +pub extern crate debug; + #[cfg(feature = "arch-riscv")] pub extern crate arch_riscv; diff --git a/docs/troubleshooting/README.md b/docs/troubleshooting/README.md new file mode 100644 index 0000000..b7f4853 --- /dev/null +++ b/docs/troubleshooting/README.md @@ -0,0 +1,10 @@ +# Troubleshooting + +This directory contains documentation for known issues, pitfalls, and their +solutions when working with ZeroOS. + +## Index + +| Issue | Affects | Symptom | +| ---------------------------------------------------- | ---------------------------------- | ------------------------------ | +| [LLVM MachineOutliner Bug](llvm-machine-outliner.md) | RISC-V builds with `-Copt-level=z` | Program hangs in infinite loop | diff --git a/docs/troubleshooting/llvm-machine-outliner.md b/docs/troubleshooting/llvm-machine-outliner.md new file mode 100644 index 0000000..bb4b053 --- /dev/null +++ b/docs/troubleshooting/llvm-machine-outliner.md @@ -0,0 +1,185 @@ +# LLVM MachineOutliner Bug on RISC-V + +## Summary + +LLVM's MachineOutliner optimization generates incorrect code on RISC-V that +causes infinite loops. This affects any Rust code compiled with `-Copt-level=z` +(optimize for size). + +**Discovered**: 2026-01-21 **Affects**: All RISC-V targets (spike, real +hardware, zkVM emulators) + +## Background + +### What is `-Copt-level=z`? + +Rust's `-Copt-level` flag controls how aggressively the compiler optimizes code. +The available levels are: + +| Level | Description | +| ----- | -------------------------------------------------- | +| `0` | No optimizations (fastest compile, largest binary) | +| `1` | Basic optimizations | +| `2` | Most optimizations (good balance) | +| `3` | All optimizations including expensive ones | +| `s` | Optimize for binary size | +| `z` | Optimize aggressively for binary size | + +The `z` level enables all size optimizations, including the MachineOutliner. + +### What is the MachineOutliner? + +The MachineOutliner is an LLVM optimization pass that reduces binary size by +identifying repeated instruction sequences and extracting them into shared +functions. For example: + +``` +// Before outlining (repeated code in multiple places) +function A: function B: + mov x1, x2 mov x1, x2 + add x3, x4 add x3, x4 + store x5 store x5 + ... ... + +// After outlining (shared function) +function A: function B: OUTLINED_FUNCTION_1: + call OUTLINED_1 call OUTLINED_1 mov x1, x2 + ... ... add x3, x4 + store x5 + ret +``` + +This can significantly reduce binary size in code with repeated patterns. + +## Symptoms + +- Program hangs indefinitely at seemingly random points +- No crash or error message +- Works correctly without `-Copt-level=z` +- Binary contains many `OUTLINED_FUNCTION_*` symbols + +## Root Cause + +On RISC-V, the MachineOutliner generates a faulty calling pattern: + +### The Bug Pattern + +```asm +# Caller code (generated by outliner) +auipc t1, 0 # t1 = PC (address of this instruction) +jr offset(t1) # Jump to outlined function + +# Outlined function +... # Function body +jr t1 # Return to caller +``` + +### Why It Fails + +1. `auipc t1, 0` sets `t1` to the address of the `auipc` instruction itself +2. The outlined function returns via `jr t1` +3. This jumps back to the `auipc` instruction, not the instruction after `jr` +4. The `auipc` executes again, setting `t1` to the same address +5. The cycle repeats infinitely + +### Expected Behavior + +The outliner should either: + +- Use `t1` to store the return address (PC + 8, after the `jr`) +- Or use a different register for the call target + +## Solution + +Disable the MachineOutliner by adding this rustflag: + +``` +-Cllvm-args=-enable-machine-outliner=never +``` + +## Trade-offs + +Disabling the outliner increases binary size. The outliner exists specifically +to reduce code size by factoring out common sequences. Depending on your binary: + +- **Small increase**: If your code has few repeated sequences +- **Significant increase**: If your code has many similar functions (e.g., + generated code, macros that expand similarly) + +If binary size is critical, consider: + +1. Using `-Copt-level=s` instead of `-Copt-level=z` (less aggressive, may not + trigger outliner) +2. Waiting for the LLVM bug to be fixed upstream +3. Using link-time optimization (LTO) for size reduction instead + +## Related LLVM Issues + +- [Bug #167381](https://github.com/llvm/llvm-project/issues/167381): Cannot + relax `auipc` + `jr` after Machine Outliner +- [PR #127659](https://github.com/llvm/llvm-project/pull/127659): Fixed + correctness bug in candidate analysis +- [PR #115867](https://github.com/llvm/llvm-project/pull/115867): Deprioritize + X5 register to help outliner + +## Technical Details + +### RISC-V Calling Convention + +The outliner is supposed to use `t0` (x5) as the return address register because +it's a caller-saved temporary. However, the code generation uses `t1` (x6) and +incorrectly calculates the return address. + +### Affected LLVM Versions + +This bug has been observed in: + +- LLVM 17, 18, 19 (bundled with Rust 1.75+) +- rustc 1.83.0, 1.90.0 (confirmed) + +## Reproduction + +### Requirements + +The MachineOutliner only triggers on **non-trivial codebases** with repeated +code patterns. A minimal "hello world" program will NOT trigger the bug because: + +1. The outliner needs substantial repeated instruction sequences to extract +2. Small programs don't have enough code to benefit from outlining +3. The bug manifests only when outlined functions are actually generated + +### Step 1: Build std-smoke with `-Copt-level=z` + +Use `CARGO_ENCODED_RUSTFLAGS` environment variable to pass the flag: + +```bash +CARGO_ENCODED_RUSTFLAGS="-Copt-level=z" cargo spike build -p std-smoke \ + --target "riscv64imac-zero-linux-musl" \ + --mode std \ + --backtrace=enable \ + -- --features=std,backtrace,with-spike --profile dev +``` + +> **Note:** `cargo spike build` merges `CARGO_ENCODED_RUSTFLAGS` with its +> internal flags, so user-provided rustflags are preserved. + +### Step 2: Verify outlined functions were generated + +```bash +nm target/riscv64imac-zero-linux-musl/debug/std-smoke | grep -c OUTLINED_FUNCTION +``` + +### Step 3: Run with cargo spike run - observe the hang + +```bash +cargo spike run target/riscv64imac-zero-linux-musl/debug/std-smoke \ + --isa RV64IMAC \ + --instructions 5000000 \ + -l +``` + +## References + +- [LLVM MachineOutliner documentation](https://llvm.org/docs/LinkTimeOptimization.html) +- [RISC-V Calling Convention](https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf) +- [Rust optimization levels](https://doc.rust-lang.org/rustc/codegen-options/index.html#opt-level) diff --git a/docs/zkvm-integration.md b/docs/zkvm-integration.md new file mode 100644 index 0000000..de212b1 --- /dev/null +++ b/docs/zkvm-integration.md @@ -0,0 +1,420 @@ +# zkVM Integration with ZeroOS + +This document describes how zero-knowledge virtual machines (zkVMs) can build on +and integrate with ZeroOS, using [Jolt](https://github.com/a16z/jolt) as a +reference implementation. + +## Overview + +ZeroOS provides a modular operating system foundation that zkVMs can use to: + +- Execute guest programs with standard library support +- Handle Linux syscalls deterministically +- Manage memory allocation and virtual file systems +- Provide threading and random number generation + +The integration is feature-driven, allowing zkVMs to include only the subsystems +they need. + +## Reference Implementation: Jolt + +- **Repository**: https://github.com/LayerZero-Research/jolt.git (fork of + https://github.com/a16z/jolt.git) +- **Branch**: `gx/wip_csr` + +## Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ Guest Program │ +│ (provable code / application logic) │ +├─────────────────────────────────────────────────────────┤ +│ jolt-sdk │ +│ ┌────────────┐ ┌────────┐ ┌────────┐ ┌──────────┐ │ +│ │ Cargo.toml │ │ boot.rs│ │ trap.rs│ │ ecall.rs │ │ +│ └────────────┘ └────────┘ └────────┘ └──────────┘ │ +├─────────────────────────────────────────────────────────┤ +│ Language + dependency libraries │ +│ Rust core/alloc/std + third-party crates (e.g. rayon) │ +├───────────────────────────┬─────────────────────────────┤ +│ Runtime │ Runtime │ +│ ┌─────────────────────┐ │ ┌───────────────────────┐ │ +│ │ runtime-nostd │ │ │ runtime-musl │ │ +│ │ (no libc) │ │ │ (musl libc, Linux │ │ +│ │ │ │ │ syscall ABI) │ │ +│ └─────────────────────┘ │ └───────────────────────┘ │ +├───────────────────────────┼─────────────────────────────┤ +│ │ ZeroOS kernel │ +│ │ ┌───────────────────────┐ │ +│ │ │ os-linux │ │ +│ │ │ (Linux syscall tbl) │ │ +│ ┌─────────────────────┐ │ ├───────────────────────┤ │ +│ │ optional subsystems │ │ │ optional subsystems │ │ +│ │ (vfs/random) │ │ │ (vfs/sched/random) │ │ +│ │ + devices │ │ │ + devices (console) │ │ +│ ├─────────────────────┤ │ ├───────────────────────┤ │ +│ │ foundation │ │ │ foundation │ │ +│ │ - memory,vfs,random │ │ │ - memory,vfs,sched, │ │ +│ │ │ │ │ random │ │ +│ └─────────────────────┘ │ └───────────────────────┘ │ +├───────────────────────────┴─────────────────────────────┤ +│ Hardware / Emulator (zkVM) │ +└─────────────────────────────────────────────────────────┘ +``` + +## Execution Flow + +### Boot Sequence (nostd Mode) + +``` +_start (linker entry) + → __platform_bootstrap() + → zeroos::initialize() + → memory::kinit(__heap_start, __heap_end - __heap_start) + → __runtime_bootstrap() + → __main_entry() + → main() +``` + +### Boot Sequence (std Mode / musl) + +``` +_start (linker entry) + → __platform_bootstrap() + → zeroos::initialize() + → memory::kinit(__heap_start, __heap_end - __heap_start) + → install_trap_vector() + → scheduler::kinit() (if thread feature) + → vfs::kinit() (if vfs feature) + → random::kinit(0) (if random feature) + → __runtime_bootstrap() + → __libc_start_main(__main_entry, ...) + → __main_entry() + → main() +``` + +### Syscall Flow (std Mode) + +Guest programs make Linux syscalls via the RISC-V `ecall` instruction: + +``` +ecall (a7 = syscall number, a0-a5 = args) + → trap_handler() + → zeroos::foundation::kfn::trap::ksyscall() + → zeroos::os::linux::linux_handle() + → syscall handler (read, write, mmap, etc.) + → return to guest +``` + +## Integration Points + +### 1. Linker Script + +The linker script defines memory layout and exports symbols used by the runtime +and SDK. + +#### Memory Layout + +``` +┌─────────────────────┐ ← __heap_end / __memory_end +│ │ +│ Heap │ (grows upward) +│ │ +├─────────────────────┤ ← __heap_start +├─────────────────────┤ ← __stack_top (initial SP) +│ │ +│ Stack │ (grows downward) +│ │ +├─────────────────────┤ ← __stack_bottom +│ Stack Canary │ (128 bytes) +├─────────────────────┤ +│ .bss │ +├─────────────────────┤ +│ .tbss / .tdata │ (TLS sections) +├─────────────────────┤ +│ .data/.sdata │ +├─────────────────────┤ +│ .rodata │ +├─────────────────────┤ +│ .text │ +├─────────────────────┤ +│ .text.boot │ +└─────────────────────┘ ← ORIGIN +``` + +#### Memory Region Symbols + +| Symbol | Used by | Purpose | +| ------------------- | ------------------- | ------------------------------------------------ | +| `__heap_start` | `memory::kinit()` | Start of heap region | +| `__heap_end` | `memory::kinit()` | End of heap region | +| `__stack_top` | `_start` (assembly) | Initial stack pointer (`lla sp, __stack_top`) | +| `__stack_bottom` | boot.rs (debug) | Bottom of stack (for overflow detection) | +| `__global_pointer$` | `_start` (assembly) | RISC-V gp register (`lla gp, __global_pointer$`) | + +#### Section Symbols (optional) + +| Symbol | Purpose | +| ---------------------------------------- | ------------------------------------------ | +| `__bss_start`, `__bss_end` | BSS zeroing at startup | +| `__tdata_start`, `__tdata_end` | Thread-local data template | +| `__tbss_start`, `__tbss_end` | Thread-local BSS | +| `__init_array_start`, `__init_array_end` | Constructor functions (called before main) | +| `__fini_array_start`, `__fini_array_end` | Destructor functions (called after main) | + +### 2. Platform Functions + +The SDK must implement platform-specific functions to wire ZeroOS into the zkVM. + +#### Required Symbols + +| Symbol | Provided by | Required | Purpose | +| ---------------------- | ------------------ | --------- | ------------------------------------------------------------ | +| `__platform_bootstrap` | boot.rs | Yes | Called by runtime before `main()` | +| `trap_handler` | trap.rs | Yes | Called by `_default_trap_handler` to handle traps | +| `platform_exit` | mod.rs | Yes | Called by `foundation::kfn::kexit()` for program termination | +| `_start` | runtime (assembly) | No (weak) | Program entry point, can override default | +| `_trap_handler` | runtime (assembly) | No (weak) | Trap vector address, can override default | + +#### Required: `__platform_bootstrap()` (boot.rs) + +Always required. Called before `main()`: + +```rust +extern "C" { + static __heap_start: u8; + static __heap_end: u8; + static __stack_top: u8; + static __stack_bottom: u8; +} + +#[inline(always)] +#[cfg(all(target_arch ="riscv64", target_os = "linux"))] +fn install_trap_vector() { + unsafe { + core::arch::asm!("la t0, _trap_handler", "csrw mtvec, t0"); + } +} + +#[no_mangle] +pub extern "C" fn __platform_bootstrap() { + zeroos::initialize(); + + // Initialize heap allocator + { + let heap_start = core::ptr::addr_of!(__heap_start) as usize; + let heap_end = core::ptr::addr_of!(__heap_end) as usize; + let heap_size = heap_end - heap_start; + zeroos::foundation::kfn::memory::kinit(heap_start, heap_size); + } + + #[cfg(not(target_os = "none"))] + { + install_trap_vector(); + + #[cfg(feature = "zeroos-thread")] + let boot_thread_anchor: usize = { + let anchor = zeroos::foundation::kfn::scheduler::kinit(); + unsafe { + core::arch::asm!("mv tp, {0}", in(reg) anchor); + core::arch::asm!("csrw mscratch, x0"); + } + anchor + }; + + #[cfg(feature = "zeroos-vfs")] + { + zeroos::foundation::kfn::vfs::kinit(); + + #[cfg(feature = "zeroos-vfs-device-console")] + { + register_console_fd(1, &STDOUT_FOPS); + register_console_fd(2, &STDERR_FOPS); + } + } + + #[cfg(feature = "zeroos-random")] + { + // RNG seed is fixed (0) for deterministic ZK proofs + zeroos::foundation::kfn::random::kinit(0); + } + + // Before entering libc: park anchor in mscratch for user trap handling + #[cfg(feature = "zeroos-thread")] + unsafe { + core::arch::asm!("csrw mscratch, {0}", in(reg) boot_thread_anchor); + core::arch::asm!("mv tp, x0"); + } + } +} +``` + +#### Required for std mode: `trap_handler()` (trap.rs) + +Routes CPU traps to ZeroOS syscall handling: + +```rust +use zeroos::arch::riscv::TrapFrame; +use riscv::register::mcause::Exception; + +#[inline(always)] +fn mcause_is_interrupt(mcause: usize) -> bool { + mcause >> (usize::BITS as usize - 1) != 0 +} + +#[inline(always)] +fn mcause_code(mcause: usize) -> usize { + mcause & ((1usize << (usize::BITS as usize - 1)) - 1) +} + +#[inline(always)] +fn advance_mepc_for_breakpoint(regs: *mut TrapFrame) { + unsafe { + let pc = (*regs).mepc; + (*regs).mepc = pc.wrapping_add(instr_len(pc)); + } +} + +#[inline(always)] +fn instr_len(addr: usize) -> usize { + let halfword = unsafe { core::ptr::read_unaligned(addr as *const u16) }; + if (halfword & 0b11) == 0b11 { 4 } else { 2 } +} + +#[no_mangle] +pub unsafe extern "C" fn trap_handler(regs: *mut u8) { + let regs = regs as *mut TrapFrame; + let mcause = (*regs).mcause; + if mcause_is_interrupt(mcause) { + return; + } + + match mcause_code(mcause) { + // Handle envcalls (syscalls) from any privilege mode + code if code == (Exception::UserEnvCall as usize) + || code == (Exception::SupervisorEnvCall as usize) + || code == (Exception::MachineEnvCall as usize) => + { + let pc = (*regs).mepc; + (*regs).mepc = pc + 4; + + let ret = zeroos::foundation::kfn::trap::ksyscall( + (*regs).a0, + (*regs).a1, + (*regs).a2, + (*regs).a3, + (*regs).a4, + (*regs).a5, + (*regs).a7, + ); + (*regs).a0 = ret as usize; + } + code if code == (Exception::Breakpoint as usize) => { + advance_mepc_for_breakpoint(regs); + } + code => { + zeroos::foundation::kfn::kexit(code as i32); + } + } +} +``` + +### 3. SDK Configuration (Cargo.toml) + +The SDK crate serves multiple build contexts: guest programs (std and nostd +modes) and host/prover programs. Feature flags control which ZeroOS subsystems +are included. + +#### Build Context Features + +The SDK defines three primary features for different build contexts: + +```toml +host = [ + "jolt-core/default", + "jolt-platform/std", + "dep:tracer", + "dep:common", + ... +] + +guest-nostd = [ + "zeroos-nostd", +] + +guest-std = [ + "postcard/use-std", + "jolt-platform/std", + "jolt-sdk-macros/guest-std", + "serde/std", + "zeroos-std", +] +``` + +- `host`: For prover/verifier programs running on the host machine +- `guest-nostd`: Minimal guest without libc (uses `zeroos-runtime-nostd`) +- `guest-std`: Full std support via musl libc (uses `zeroos-runtime-musl`) + +#### Target-Conditional Dependencies + +ZeroOS features are activated based on target architecture and OS. This ensures +the correct subsystems are included only when compiling for the zkVM target: + +```toml +[target.'cfg(all(target_arch = "riscv64"))'.dependencies] +riscv = { workspace = true } +zeroos = { workspace = true, default-features = false, features = ["debug", "arch-riscv", "alloc-linked-list"]} + +[target.'cfg(all(target_arch = "riscv64", target_os = "linux"))'.dependencies] +zeroos = { workspace = true, default-features = false, features = ["os-linux"]} +``` + +- `target_arch = "riscv64"`: Enables RISC-V architecture support and heap + allocator +- `target_os = "linux"`: Adds Linux syscall emulation (required for std mode) + +#### Subsystem Feature Aliases + +The SDK defines `zeroos-*` features for internal use in `support/` code, with +user-friendly aliases: + +```toml +# User-facing aliases +thread = ["zeroos-thread"] +random = ["zeroos-random"] +stdout = ["zeroos-vfs-device-console"] + +# Internal features (used by boot.rs via cfg(feature = "zeroos-...")) +zeroos-runtime-nostd = ["zeroos?/runtime-nostd"] +zeroos-runtime-musl = ["zeroos?/runtime-musl"] +zeroos-thread = ["zeroos?/scheduler-cooperative"] +zeroos-vfs = ["zeroos?/vfs"] +zeroos-random = ["zeroos?/random", "zeroos?/rng-lcg"] +zeroos-vfs-device-console = ["zeroos-vfs", "zeroos?/vfs-device-console"] +``` + +This naming convention: + +- Avoids conflicts with other crate-specific features +- Provides simple aliases (`thread`, `random`, `stdout`) that hide ZeroOS + implementation details +- Enables conditional compilation in `support/` code via + `#[cfg(feature = "zeroos-thread")]` + +### 4. Build Infrastructure + +The build crate provides a build command (similar syntax to `cargo build`) and +uses `zeroos-build` to standardize how guest programs are compiled and linked +for the zkVM: + +```toml +[dependencies] +zeroos-build.workspace = true +``` + +Key responsibilities: + +- Toolchain management (e.g. musl toolchain for `guest-std`) +- Target/ABI configuration (target spec / target triple selection) +- Compile and link options (rustflags, linker args, linker script generation) +- Reproducible guest builds (stable paths/layout, deterministic configuration) diff --git a/examples/fibonacci/Cargo.toml b/examples/fibonacci/Cargo.toml index f756881..5204d83 100644 --- a/examples/fibonacci/Cargo.toml +++ b/examples/fibonacci/Cargo.toml @@ -10,7 +10,7 @@ debug.workspace = true cfg-if.workspace = true [features] -default = ["with-spike"] +default = [] debug = ["platform/debug"] diff --git a/examples/std-smoke/Cargo.toml b/examples/std-smoke/Cargo.toml index 7b625d9..b914fa7 100644 --- a/examples/std-smoke/Cargo.toml +++ b/examples/std-smoke/Cargo.toml @@ -13,18 +13,10 @@ rayon.workspace = true libc.workspace = true [features] -default = ["with-spike", "memory"] +default = ["memory"] with-spike = ["platform/with-spike"] -std = [ - "with-spike", - "platform/std", - "debug", - "memory", - "vfs", - "bounds-checks", - "thread", -] +std = ["platform/std", "debug", "memory", "vfs", "bounds-checks", "thread"] debug = ["platform/debug"] memory = ["platform/memory"] vfs = ["platform/vfs"] @@ -35,5 +27,3 @@ backtrace = ["platform/backtrace"] [[bin]] name = "std-smoke" path = "src/main.rs" - - diff --git a/platforms/spike-build/src/cmds/build.rs b/platforms/spike-build/src/cmds/build.rs index 615cccb..316519b 100644 --- a/platforms/spike-build/src/cmds/build.rs +++ b/platforms/spike-build/src/cmds/build.rs @@ -46,23 +46,15 @@ pub fn build_command(args: SpikeBuildArgs) -> Result<()> { let toolchain_paths = if args.base.mode == StdMode::Std || fully { let tc_cfg = build::toolchain::ToolchainConfig::default(); let install_cfg = build::toolchain::InstallConfig::default(); - let paths = match build::toolchain::get_or_install_toolchain( + let paths = build::toolchain::get_or_install_or_build_toolchain( args.base.musl_lib_path.clone(), args.base.gcc_lib_path.clone(), &tc_cfg, &install_cfg, - ) { - Ok(p) => (p.musl_lib, p.gcc_lib), - Err(e) => { - eprintln!("Toolchain install failed: {}", e); - eprintln!("Falling back to building toolchain from source..."); - build::cmds::get_or_build_toolchain( - args.base.musl_lib_path.clone(), - args.base.gcc_lib_path.clone(), - fully, - )? - } - }; + fully, + ) + .map(|p| (p.musl_lib, p.gcc_lib)) + .map_err(|e| anyhow::anyhow!("Toolchain setup failed: {}", e))?; Some(paths) } else { None diff --git a/platforms/spike-platform/src/trap.rs b/platforms/spike-platform/src/trap.rs index 4a071a4..8469f72 100644 --- a/platforms/spike-platform/src/trap.rs +++ b/platforms/spike-platform/src/trap.rs @@ -53,6 +53,9 @@ pub unsafe extern "C" fn trap_handler(regs: *mut u8) { let pc = (*regs).mepc; (*regs).mepc = pc + 4; + #[cfg(feature = "debug")] + debug::writeln!("[syscall] {}", zeroos::os::linux::syscall_name((*regs).a7)); + let ret = foundation::kfn::trap::ksyscall( (*regs).a0, (*regs).a1, @@ -68,7 +71,7 @@ pub unsafe extern "C" fn trap_handler(regs: *mut u8) { advance_mepc_for_breakpoint(regs); } code => { - htif::exit(code as u32); + foundation::kfn::kexit(code as i32); } } }