346 lines
9.4 KiB
Rust
346 lines
9.4 KiB
Rust
//! # 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
|
|
|
|
// Useful for adding comments about different branches
|
|
#![allow(clippy::if_same_then_else)]
|
|
// Useful in the syscall function
|
|
#![allow(clippy::many_single_char_names)]
|
|
// Used for context::context
|
|
#![allow(clippy::module_inception)]
|
|
// Not implementing default is sometimes useful in the case something has significant cost
|
|
// to allocate. If you implement default, it can be allocated without evidence using the
|
|
// ..Default::default() syntax. Not fun in kernel space
|
|
#![allow(clippy::new_without_default)]
|
|
// Used to make it nicer to return errors, for example, .ok_or(Error::new(ESRCH))
|
|
#![allow(clippy::or_fun_call)]
|
|
// This is needed in some cases, like for syscall
|
|
#![allow(clippy::too_many_arguments)]
|
|
// There is no harm in this being done
|
|
#![allow(clippy::useless_format)]
|
|
|
|
// TODO: address ocurrances and then deny
|
|
#![warn(clippy::not_unsafe_ptr_arg_deref)]
|
|
// TODO: address ocurrances and then deny
|
|
#![warn(clippy::cast_ptr_alignment)]
|
|
// Indexing a slice can cause panics and that is something we always want to avoid
|
|
// in kernel code. Use .get and return an error instead
|
|
// TODO: address ocurrances and then deny
|
|
#![warn(clippy::indexing_slicing)]
|
|
// Overflows are very, very bad in kernel code as it may provide an attack vector for
|
|
// userspace applications, and it is only checked in debug builds
|
|
// TODO: address ocurrances and then deny
|
|
#![warn(clippy::integer_arithmetic)]
|
|
// Avoid panicking in the kernel without information about the panic. Use expect
|
|
// TODO: address ocurrances and then deny
|
|
#![warn(clippy::result_unwrap_used)]
|
|
|
|
// This is usually a serious issue - a missing import of a define where it is interpreted
|
|
// as a catch-all variable in a match, for example
|
|
#![deny(unreachable_patterns)]
|
|
// Ensure that all must_use results are used
|
|
#![deny(unused_must_use)]
|
|
|
|
#![feature(allocator_api)]
|
|
#![feature(asm_const)] // TODO: Relax requirements of most asm invocations
|
|
#![feature(int_roundings)]
|
|
#![feature(let_chains)]
|
|
#![feature(naked_functions)]
|
|
#![feature(new_uninit)]
|
|
#![feature(offset_of)]
|
|
#![feature(sync_unsafe_cell)]
|
|
#![feature(variant_count)]
|
|
#![no_std]
|
|
#![no_main]
|
|
|
|
#[macro_use]
|
|
extern crate alloc;
|
|
|
|
#[macro_use]
|
|
extern crate bitflags;
|
|
|
|
use core::sync::atomic::{AtomicU32, Ordering};
|
|
|
|
use crate::scheme::SchemeNamespace;
|
|
|
|
use crate::consts::*;
|
|
|
|
#[macro_use]
|
|
/// Shared data structures
|
|
mod common;
|
|
|
|
/// Architecture-dependent stuff
|
|
#[macro_use]
|
|
mod arch;
|
|
use crate::arch::*;
|
|
|
|
use crate::log::info;
|
|
|
|
/// Heap allocators
|
|
mod allocator;
|
|
|
|
/// ACPI table parsing
|
|
#[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))]
|
|
mod acpi;
|
|
|
|
#[cfg(all(any(target_arch = "aarch64")))]
|
|
mod dtb;
|
|
|
|
/// Context management
|
|
mod context;
|
|
|
|
/// Debugger
|
|
mod debugger;
|
|
|
|
/// Architecture-independent devices
|
|
mod devices;
|
|
|
|
/// ELF file parsing
|
|
#[cfg(not(feature="doc"))]
|
|
mod elf;
|
|
|
|
/// Event handling
|
|
mod event;
|
|
|
|
/// External functions
|
|
mod externs;
|
|
|
|
/// Logging
|
|
mod log;
|
|
|
|
/// Memory management
|
|
mod memory;
|
|
|
|
/// Panic
|
|
#[cfg(not(any(feature="doc", test)))]
|
|
mod panic;
|
|
|
|
mod percpu;
|
|
|
|
/// Process tracing
|
|
mod ptrace;
|
|
|
|
/// Performance profiling of the kernel
|
|
pub mod profiling;
|
|
|
|
/// Schemes, filesystem handlers
|
|
mod scheme;
|
|
|
|
/// Synchronization primitives
|
|
mod sync;
|
|
|
|
/// Syscall handlers
|
|
mod syscall;
|
|
|
|
/// Time
|
|
mod time;
|
|
|
|
/// Tests
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
#[global_allocator]
|
|
static ALLOCATOR: allocator::Allocator = allocator::Allocator;
|
|
|
|
/// Get the current CPU's scheduling ID
|
|
#[inline(always)]
|
|
fn cpu_id() -> LogicalCpuId {
|
|
crate::percpu::PercpuBlock::current().cpu_id
|
|
}
|
|
|
|
/// The count of all CPUs that can have work scheduled
|
|
static CPU_COUNT: AtomicU32 = AtomicU32::new(0);
|
|
|
|
/// Get the number of CPUs currently active
|
|
#[inline(always)]
|
|
fn cpu_count() -> u32 {
|
|
CPU_COUNT.load(Ordering::Relaxed)
|
|
}
|
|
|
|
fn init_env() -> &'static [u8] {
|
|
crate::BOOTSTRAP.get().expect("BOOTSTRAP was not set").env
|
|
}
|
|
|
|
extern "C" fn userspace_init() {
|
|
let bootstrap = crate::BOOTSTRAP.get().expect("BOOTSTRAP was not set");
|
|
unsafe { crate::syscall::process::usermode_bootstrap(bootstrap) }
|
|
}
|
|
|
|
struct Bootstrap {
|
|
base: crate::memory::Frame,
|
|
page_count: usize,
|
|
entry: usize,
|
|
env: &'static [u8],
|
|
}
|
|
static BOOTSTRAP: spin::Once<Bootstrap> = spin::Once::new();
|
|
|
|
/// This is the kernel entry point for the primary CPU. The arch crate is responsible for calling this
|
|
fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! {
|
|
CPU_COUNT.store(cpu_count, Ordering::SeqCst);
|
|
|
|
//Initialize the first context, stored in kernel/src/context/mod.rs
|
|
context::init();
|
|
|
|
//Initialize global schemes, such as `acpi:`.
|
|
scheme::init_globals();
|
|
|
|
let pid = syscall::getpid();
|
|
info!("BSP: {:?} {}", pid, cpu_count);
|
|
info!("Env: {:?}", ::core::str::from_utf8(bootstrap.env));
|
|
|
|
BOOTSTRAP.call_once(|| bootstrap);
|
|
profiling::ready_for_profiling();
|
|
|
|
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;
|
|
context.name = "bootstrap".into();
|
|
},
|
|
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)]
|
|
fn kmain_ap(cpu_id: LogicalCpuId) -> ! {
|
|
profiling::maybe_run_profiling_helper_forever(cpu_id);
|
|
|
|
if cfg!(feature = "multi_core") {
|
|
context::init();
|
|
|
|
let pid = syscall::getpid();
|
|
info!("AP {}: {:?}", cpu_id, pid);
|
|
|
|
profiling::ready_for_profiling();
|
|
|
|
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 {
|
|
info!("AP {}: Disabled", cpu_id);
|
|
|
|
loop {
|
|
unsafe {
|
|
interrupt::disable();
|
|
interrupt::halt();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Allow exception handlers to send signal to arch-independant kernel
|
|
#[no_mangle]
|
|
extern fn ksignal(signal: usize) {
|
|
info!("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();
|
|
info!("NAME {}", context.name);
|
|
}
|
|
}
|
|
|
|
// Try running kill(getpid(), signal), but fallback to exiting
|
|
syscall::getpid()
|
|
.and_then(|pid| syscall::kill(pid, signal).map(|_| ()))
|
|
.unwrap_or_else(|_| {
|
|
syscall::exit(signal & 0x7F);
|
|
});
|
|
}
|
|
|
|
// TODO: Use this macro on aarch64 too.
|
|
|
|
macro_rules! linker_offsets(
|
|
($($name:ident),*) => {
|
|
$(
|
|
#[inline]
|
|
pub fn $name() -> usize {
|
|
extern "C" {
|
|
// TODO: UnsafeCell?
|
|
static $name: u8;
|
|
}
|
|
unsafe { &$name as *const u8 as usize }
|
|
}
|
|
)*
|
|
}
|
|
);
|
|
mod kernel_executable_offsets {
|
|
linker_offsets!(__text_start, __text_end, __rodata_start, __rodata_end, __data_start, __data_end, __bss_start, __bss_end, __usercopy_start, __usercopy_end);
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
linker_offsets!(__altrelocs_start, __altrelocs_end);
|
|
}
|
|
|
|
/// A unique number used internally by the kernel to identify CPUs.
|
|
///
|
|
/// This is usually but not necessarily the same as the APIC ID.
|
|
|
|
// TODO: Differentiate between logical CPU IDs and hardware CPU IDs (e.g. APIC IDs)
|
|
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
|
// TODO: NonMaxUsize?
|
|
// TODO: Optimize away this type if not cfg!(feature = "multi_core")
|
|
struct LogicalCpuId(u32);
|
|
|
|
impl LogicalCpuId {
|
|
const BSP: Self = Self::new(0);
|
|
|
|
const fn new(inner: u32) -> Self { Self(inner) }
|
|
const fn get(self) -> u32 { self.0 }
|
|
}
|
|
|
|
impl core::fmt::Debug for LogicalCpuId {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
write!(f, "[logical cpu #{}]", self.0)
|
|
}
|
|
}
|
|
impl core::fmt::Display for LogicalCpuId {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
write!(f, "#{}", self.0)
|
|
}
|
|
}
|
|
|
|
// TODO: Support more than 128 CPUs.
|
|
// The maximum number of CPUs on Linux is configurable, and the type for LogicalCpuSet and
|
|
// LogicalCpuId may be optimized accordingly. In that case, box the mask if it's larger than some
|
|
// base size (probably 256 bytes).
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
struct LogicalCpuSet(u128);
|
|
|
|
impl LogicalCpuSet {
|
|
const fn new(inner: u128) -> Self { Self(inner) }
|
|
const fn get(self) -> u128 { self.0 }
|
|
|
|
const fn empty() -> Self { Self::new(0) }
|
|
const fn all() -> Self { Self::new(!0) }
|
|
const fn single(id: LogicalCpuId) -> Self { Self::new(1 << id.get()) }
|
|
const fn contains(&self, id: LogicalCpuId) -> bool {
|
|
self.0 & (1 << id.get()) != 0
|
|
}
|
|
}
|