//! # The Redox OS Kernel, version 2 //! //! The Redox OS Kernel is a microkernel that supports `x86_64` systems and //! provides Unix-like syscalls for primarily Rust applications //#![deny(warnings)] #![cfg_attr(feature = "clippy", allow(if_same_then_else))] #![cfg_attr(feature = "clippy", allow(inline_always))] #![cfg_attr(feature = "clippy", allow(many_single_char_names))] #![cfg_attr(feature = "clippy", allow(module_inception))] #![cfg_attr(feature = "clippy", allow(new_without_default))] #![cfg_attr(feature = "clippy", allow(not_unsafe_ptr_arg_deref))] #![cfg_attr(feature = "clippy", allow(or_fun_call))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))] #![deny(unreachable_patterns)] #![feature(alloc)] #![feature(allocator_api)] #![feature(asm)] #![feature(concat_idents)] #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(integer_atomics)] #![feature(lang_items)] #![feature(naked_functions)] #![feature(never_type)] #![feature(ptr_internals)] #![feature(thread_local)] #![no_std] pub extern crate x86; #[macro_use] extern crate alloc; #[macro_use] extern crate bitflags; extern crate goblin; extern crate linked_list_allocator; extern crate rustc_demangle; extern crate spin; #[cfg(feature = "slab")] extern crate slab_allocator; use alloc::vec::Vec; use core::sync::atomic::{AtomicUsize, Ordering}; use crate::scheme::{FileHandle, SchemeNamespace}; pub use crate::consts::*; #[macro_use] /// Shared data structures pub mod common; /// Architecture-dependent stuff #[macro_use] pub mod arch; pub use crate::arch::*; /// Constants like memory locations pub mod consts; /// Heap allocators pub mod allocator; /// ACPI table parsing #[cfg(feature = "acpi")] mod acpi; /// Context management pub mod context; /// Architecture-independent devices pub mod devices; /// ELF file parsing #[cfg(not(feature="doc"))] pub mod elf; /// Event handling pub mod event; /// External functions pub mod externs; /// Logging pub mod log; /// Memory management pub mod memory; /// Panic #[cfg(not(any(feature="doc", test)))] pub mod panic; /// Process tracing pub mod ptrace; /// Schemes, filesystem handlers pub mod scheme; /// Synchronization primitives pub mod sync; /// Syscall handlers pub mod syscall; /// Time pub mod time; /// Tests #[cfg(test)] pub mod tests; #[global_allocator] static ALLOCATOR: allocator::Allocator = allocator::Allocator; /// A unique number that identifies the current CPU - used for scheduling #[thread_local] static CPU_ID: AtomicUsize = AtomicUsize::new(0); /// Get the current CPU's scheduling ID #[inline(always)] pub fn cpu_id() -> usize { CPU_ID.load(Ordering::Relaxed) } /// The count of all CPUs that can have work scheduled static CPU_COUNT : AtomicUsize = AtomicUsize::new(0); /// Get the number of CPUs currently active #[inline(always)] pub fn cpu_count() -> usize { CPU_COUNT.load(Ordering::Relaxed) } static mut INIT_ENV: &[u8] = &[]; /// Initialize userspace by running the initfs:bin/init process /// This function will also set the CWD to initfs:bin and open debug: as stdio pub extern fn userspace_init() { let path = b"initfs:/bin/init"; let env = unsafe { INIT_ENV }; assert_eq!(syscall::chdir(b"initfs:"), Ok(0)); assert_eq!(syscall::open(b"debug:", syscall::flag::O_RDONLY).map(FileHandle::into), Ok(0)); assert_eq!(syscall::open(b"debug:", syscall::flag::O_WRONLY).map(FileHandle::into), Ok(1)); assert_eq!(syscall::open(b"debug:", syscall::flag::O_WRONLY).map(FileHandle::into), Ok(2)); let fd = syscall::open(path, syscall::flag::O_RDONLY).expect("failed to open init"); let mut args = Vec::new(); args.push(path.to_vec().into_boxed_slice()); let mut vars = Vec::new(); for var in env.split(|b| *b == b'\n') { if ! var.is_empty() { vars.push(var.to_vec().into_boxed_slice()); } } syscall::fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None).expect("failed to execute init"); panic!("init returned"); } /// This is the kernel entry point for the primary CPU. The arch crate is responsible for calling this pub fn kmain(cpus: usize, env: &'static [u8]) -> ! { CPU_ID.store(0, Ordering::SeqCst); CPU_COUNT.store(cpus, Ordering::SeqCst); unsafe { INIT_ENV = env }; //Initialize the first context, stored in kernel/src/context/mod.rs context::init(); let pid = syscall::getpid(); println!("BSP: {:?} {}", pid, cpus); println!("Env: {:?}", ::core::str::from_utf8(env)); match context::contexts_mut().spawn(userspace_init) { Ok(context_lock) => { let mut context = context_lock.write(); context.rns = SchemeNamespace::from(1); context.ens = SchemeNamespace::from(1); context.status = context::Status::Runnable; }, Err(err) => { panic!("failed to spawn userspace_init: {:?}", err); } } loop { unsafe { interrupt::disable(); if context::switch() { interrupt::enable_and_nop(); } else { // Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired. interrupt::enable_and_halt(); } } } } /// This is the main kernel entry point for secondary CPUs #[allow(unreachable_code, unused_variables)] pub fn kmain_ap(id: usize) -> ! { CPU_ID.store(id, Ordering::SeqCst); if cfg!(feature = "multi_core") { context::init(); let pid = syscall::getpid(); println!("AP {}: {:?}", id, pid); loop { unsafe { interrupt::disable(); if context::switch() { interrupt::enable_and_nop(); } else { // Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired. interrupt::enable_and_halt(); } } } } else { println!("AP {}: Disabled", id); loop { unsafe { interrupt::disable(); interrupt::halt(); } } } } /// Allow exception handlers to send signal to arch-independant kernel #[no_mangle] pub extern fn ksignal(signal: usize) { println!("SIGNAL {}, CPU {}, PID {:?}", signal, cpu_id(), context::context_id()); { let contexts = context::contexts(); if let Some(context_lock) = contexts.current() { let context = context_lock.read(); println!("NAME {}", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) }); } } syscall::exit(signal & 0x7F); }