#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(linked_list_cursors)]
#![feature(const_binary_heap_constructor)]
#![feature(btree_extract_if)]
#![feature(custom_test_frameworks)]
#![feature(byte_slice_trim_ascii)]
#![test_runner(crate::testing::test_runner)]
#![reexport_test_harness_main = "test_main"]
#![cfg_attr(test, allow(dead_code))]
#![cfg_attr(test, allow(unused_imports))]
extern crate alloc;
core::arch::global_asm!(include_str!("boot.S"));
#[macro_use]
mod macros;
mod acpi;
mod cmdline;
mod collections;
mod cpu;
mod devices;
mod executable;
mod fs;
mod graphics;
mod hw;
mod io;
mod memory_management;
mod multiboot2;
mod net;
mod panic_handler;
mod power;
mod process;
mod sync;
mod testing;
mod utils;
use alloc::vec::Vec;
use cpu::{
gdt,
interrupts::{self, apic},
};
use executable::elf::Elf;
use increasing_heap_allocator::HeapStats;
use io::console;
use kernel_user_link::{
file::{BlockingMode, OpenOptions},
FD_STDERR, FD_STDIN, FD_STDOUT,
};
use memory_management::virtual_memory_mapper;
use multiboot2::MultiBoot2Info;
use process::scheduler;
use tracing::info;
use crate::{
devices::clock,
memory_management::{
kernel_heap_allocator::ALLOCATOR,
memory_layout::{self, MemSize, KERNEL_HEAP_SIZE, PAGE_4K},
physical_page_allocator, virtual_space,
},
process::Process,
};
fn finish_boot() {
let physical_pages_stats = physical_page_allocator::stats();
let free_mem = MemSize(physical_pages_stats.0 * PAGE_4K);
let used_mem = MemSize(physical_pages_stats.1 * PAGE_4K);
let HeapStats {
allocated,
free_size,
heap_size,
} = ALLOCATOR.stats();
info!("Boot finished!");
memory_layout::display_kernel_map();
info!("Free memory: {}", free_mem);
info!(
"Used memory: {} ({:0.3}%)",
used_mem,
used_mem.0 as f64 / (used_mem.0 + free_mem.0) as f64 * 100.
);
info!("Free heap: {}", MemSize(free_size));
info!(
"Used heap: {} ({:0.3}%)",
MemSize(allocated),
allocated as f64 / heap_size as f64 * 100.
);
info!(
"From possible heap: {} ({:0.3}%)",
MemSize(KERNEL_HEAP_SIZE),
allocated as f64 / KERNEL_HEAP_SIZE as f64 * 100.
);
virtual_space::debug_blocks();
info!("");
}
fn load_init_process() {
let mut init_file = fs::File::open("/init").expect("Could not find `init` file");
let elf = Elf::load(&mut init_file).expect("Could not load init file");
let mut process = Process::allocate_process(
0,
&elf,
&mut init_file,
Vec::new(),
fs::Directory::open("/").expect("No root"),
)
.expect("Could not allocate process for `init`");
assert_eq!(process.id(), 0, "Must be the first process");
let mut console = fs::File::open_blocking(
"/devices/console",
BlockingMode::Line,
OpenOptions::READ | OpenOptions::WRITE,
)
.expect("Could not find `/devices/console`");
console.set_terminal(true);
process.attach_fs_node_to_fd(FD_STDIN, console.clone_inherit());
process.attach_fs_node_to_fd(FD_STDOUT, console.clone_inherit());
process.attach_fs_node_to_fd(FD_STDERR, console);
info!("Added `init` process pid={}", process.id());
scheduler::push_process(process);
}
#[link_section = ".text"]
#[no_mangle]
#[cfg(not(test))]
pub extern "C" fn kernel_main(multiboot_info: &'static MultiBoot2Info) -> ! {
cmdline::init(multiboot_info);
console::early_init();
console::tracing::init();
cmdline::print_cmdline_parse(multiboot_info);
info!("{}", multiboot_info);
physical_page_allocator::init(multiboot_info);
virtual_memory_mapper::init_kernel_vm();
console::tracing::move_to_dynamic_buffer();
gdt::init_kernel_gdt();
interrupts::init_interrupts();
devices::init_devices_mapping();
let bios_tables = acpi::init_acpi_tables(multiboot_info);
info!("BIOS tables: {}", bios_tables);
apic::init(bios_tables);
acpi::init();
clock::init(bios_tables);
unsafe { cpu::set_interrupts() };
devices::init_legacy_devices();
graphics::vga::init(multiboot_info.framebuffer());
console::init_late_device(multiboot_info.framebuffer());
devices::probe_pci_devices();
fs::create_disk_mapping(0).expect("Could not load filesystem");
finish_boot();
load_init_process();
scheduler::schedule();
power::finish_power_sequence();
}
#[link_section = ".text"]
#[no_mangle]
#[cfg(test)]
pub extern "C" fn kernel_main(multiboot_info: &MultiBoot2Info) -> ! {
console::early_init();
physical_page_allocator::init(multiboot_info);
virtual_memory_mapper::init_kernel_vm();
test_main();
loop {
unsafe {
cpu::halt();
}
}
}