Files
RedBear-OS/src/startup/mod.rs
T

242 lines
6.7 KiB
Rust

use core::{
hint,
ptr::NonNull,
slice,
sync::atomic::{AtomicBool, Ordering},
};
use crate::{
arch::interrupt,
context::{self, switch::SwitchResult},
memory::{PhysicalAddress, RmmA, RmmArch},
profiling, scheme,
sync::CleanLockToken,
};
use crate::numa;
pub mod memory;
#[repr(C, packed(8))]
pub(crate) struct KernelArgs {
kernel_base: u64,
kernel_size: u64,
stack_base: u64,
stack_size: u64,
env_base: u64,
env_size: u64,
/// The base pointer to the saved RSDP or device tree blob.
///
/// On x86 this field can be NULL, and if so, the system has not booted
/// with UEFI or in some other way retrieved the RSDPs. The kernel or a
/// userspace driver will thus try searching the BIOS memory instead. On
/// UEFI systems, searching is not guaranteed to actually work though.
/// On other architectures this field must always contain a pointer to
/// either an RSDP or device tree blob.
pub(crate) hwdesc_base: u64,
pub(crate) hwdesc_size: u64,
areas_base: u64,
areas_size: u64,
/// The physical base 64-bit pointer to the contiguous bootstrap/initfs.
bootstrap_base: u64,
/// Size of contiguous bootstrap/initfs physical region, not necessarily page aligned.
bootstrap_size: u64,
}
impl KernelArgs {
pub(crate) fn print(&self) {
debug!(
"Kernel: {:X}:{:X}",
{ self.kernel_base },
self.kernel_base + self.kernel_size
);
debug!(
"Env: {:X}:{:X}",
{ self.env_base },
self.env_base + self.env_size
);
debug!(
"HWDESC: {:X}:{:X}",
{ self.hwdesc_base },
self.hwdesc_base + self.hwdesc_size
);
debug!(
"Areas: {:X}:{:X}",
{ self.areas_base },
self.areas_base + self.areas_size
);
debug!(
"Bootstrap: {:X}:{:X}",
{ self.bootstrap_base },
self.bootstrap_base + self.bootstrap_size
);
}
pub(crate) fn bootstrap(&self) -> Bootstrap {
Bootstrap {
base: crate::memory::Frame::containing(crate::memory::PhysicalAddress::new(
self.bootstrap_base as usize,
)),
page_count: (self.bootstrap_size as usize) / crate::memory::PAGE_SIZE,
env: self.env(),
}
}
pub(crate) fn env(&self) -> &'static [u8] {
unsafe {
slice::from_raw_parts(
RmmA::phys_to_virt(PhysicalAddress::new(self.env_base as usize)).data()
as *const u8,
self.env_size as usize,
)
}
}
pub(crate) fn acpi_rsdp(&self) -> Option<NonNull<u8>> {
if self.hwdesc_base != 0 {
let data = unsafe {
slice::from_raw_parts(
RmmA::phys_to_virt(PhysicalAddress::new(self.hwdesc_base as usize)).data()
as *const u8,
self.hwdesc_size as usize,
)
};
if data.starts_with(b"RSD PTR ") {
Some(NonNull::from_ref(data).cast())
} else {
None
}
} else {
None
}
}
pub(crate) fn dtb(&self) -> Option<fdt::Fdt<'static>> {
if self.hwdesc_base != 0 {
let data = unsafe {
slice::from_raw_parts(
RmmA::phys_to_virt(PhysicalAddress::new(self.hwdesc_base as usize)).data()
as *const u8,
self.hwdesc_size as usize,
)
};
fdt::Fdt::new(data).ok()
} else {
None
}
}
}
pub(crate) fn init_env() -> &'static [u8] {
BOOTSTRAP.get().expect("BOOTSTRAP was not set").env
}
extern "C" fn userspace_init() {
let mut token = unsafe { CleanLockToken::new() };
let bootstrap = BOOTSTRAP.get().expect("BOOTSTRAP was not set");
unsafe { crate::syscall::process::usermode_bootstrap(bootstrap, &mut token) }
}
pub(crate) struct Bootstrap {
pub(crate) base: crate::memory::Frame,
pub(crate) page_count: usize,
env: &'static [u8],
}
static BOOTSTRAP: spin::Once<Bootstrap> = spin::Once::new();
pub(crate) static AP_READY: AtomicBool = AtomicBool::new(false);
static BSP_READY: AtomicBool = AtomicBool::new(false);
/// This is the kernel entry point for the primary CPU. The arch crate is responsible for calling this
pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
let mut token = unsafe { CleanLockToken::new() };
BSP_READY.store(true, Ordering::SeqCst);
//Initialize the first context, stored in kernel/src/context/mod.rs
context::init(&mut token);
//Initialize global schemes, such as `acpi:`.
scheme::init_globals();
debug!("BSP: {} CPUs", crate::cpu_count());
debug!("Env: {:?}", ::core::str::from_utf8(bootstrap.env));
BOOTSTRAP.call_once(|| bootstrap);
profiling::ready_for_profiling();
let owner = None; // kmain not owned by any fd
match context::spawn(true, owner, userspace_init, &mut token) {
Ok(context_lock) => {
let mut context = context_lock.write(token.token());
context.status = context::Status::Runnable;
context.name.clear();
context.name.push_str("[bootstrap]");
// TODO: Remove these from kernel
context.euid = 0;
context.egid = 0;
}
Err(err) => {
panic!("failed to spawn userspace_init: {:?}", err);
}
}
numa::dump_info();
run_userspace(&mut token)
}
/// This is the main kernel entry point for secondary CPUs
#[allow(unreachable_code, unused_variables, dead_code)]
pub(crate) fn kmain_ap(cpu_id: crate::cpu_set::LogicalCpuId) -> ! {
let mut token = unsafe { CleanLockToken::new() };
AP_READY.store(true, Ordering::SeqCst);
while !BSP_READY.load(Ordering::SeqCst) {
hint::spin_loop();
}
profiling::maybe_run_profiling_helper_forever(cpu_id);
if !cfg!(feature = "multi_core") {
debug!("AP {}: Disabled", cpu_id);
loop {
unsafe {
interrupt::disable();
interrupt::halt();
}
}
}
context::init(&mut token);
debug!("AP {}", cpu_id);
profiling::ready_for_profiling();
run_userspace(&mut token);
}
fn run_userspace(token: &mut CleanLockToken) -> ! {
loop {
unsafe {
interrupt::disable();
match context::switch(token) {
SwitchResult::Switched => {
interrupt::enable_and_nop();
}
SwitchResult::AllContextsIdle => {
interrupt::enable_and_halt();
}
}
}
}
}