#![doc = include_str!("../README.md")]
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(let_chains)]
#![feature(naked_functions)]
#![allow(static_mut_refs)]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::redundant_pattern_matching)]
#![allow(clippy::mut_from_ref)]
#![allow(clippy::new_without_default)]
use core::arch::naked_asm;
use core::panic::PanicInfo;
use core::sync::atomic::AtomicU32;
use multiboot as mb;
#[macro_use]
mod device;
#[macro_use]
mod arch;
mod assignments;
mod gdt;
#[cfg(feature = "graphics")]
mod graphics;
mod interrupts;
mod multiboot;
mod threading;
mod user;
mod util;
use device::cga::Window;
use device::{KEYBOARD, SERIAL};
use crate::arch::acpi::Acpi;
#[cfg(feature = "smp")]
use crate::arch::smp;
use crate::arch::{cpu, int};
use crate::device::{DBG, KOUT};
use crate::gdt::GDT_PTR;
use crate::interrupts::guard::GUARD;
use crate::threading::Thread;
#[cfg(feature = "smp")]
const MAX_CPUS: usize = 4;
#[cfg(not(feature = "smp"))]
const MAX_CPUS: usize = 1;
#[cfg(not(feature = "graphics"))]
const FLAGS: u32 = mb::Header::PAGE_ALIGN | mb::Header::MEM_INFO;
#[cfg(feature = "graphics")]
const FLAGS: u32 = mb::Header::PAGE_ALIGN | mb::Header::MEM_INFO | mb::Header::VIDEO_MODE;
const SCREEN_WIDTH: usize = 1280;
const SCREEN_HEIGHT: usize = 1024;
#[link_section = ".multiboot"]
#[no_mangle]
pub static MBOOT: mb::Header = mb::Header::new(FLAGS, SCREEN_WIDTH as _, SCREEN_HEIGHT as _, 32);
static mut MBOOT_PTR: u32 = 0;
#[allow(unused)]
extern "C" {
static KERNEL_BEGIN: u8;
static KERNEL_END: u8;
}
const STACK_SIZE: usize = 32 * 1024;
static STACK_OFFSET: AtomicU32 = AtomicU32::new(0);
static mut INIT_STACKS: [[u32; STACK_SIZE / 4]; MAX_CPUS] = [[0; STACK_SIZE / 4]; MAX_CPUS];
#[naked]
#[no_mangle]
#[link_section = ".inittext"]
pub unsafe extern "C" fn start() -> ! {
naked_asm!(
"mov {MBOOT_PTR}, ebx",
"lgdt {GDT_PTR}",
"ljmp 0x8, offset {start_high}",
MBOOT_PTR = sym MBOOT_PTR,
GDT_PTR = sym GDT_PTR,
start_high = sym start_high,
)
}
#[naked]
#[no_mangle]
unsafe extern "C" fn start_high() -> ! {
naked_asm!(
"mov ax, 0x10",
"mov ds, ax",
"mov es, ax",
"mov fs, ax",
"mov gs, ax",
"mov ss, ax",
"mov eax, {STACK_SIZE}",
"lock xadd {STACK_OFFSET}, eax",
"add eax, {STACK_SIZE}",
"lea esp, {INIT_STACKS}",
"add esp, eax",
"cld",
"call {main}",
STACK_SIZE = const STACK_SIZE,
STACK_OFFSET = sym STACK_OFFSET,
INIT_STACKS = sym INIT_STACKS,
main = sym main,
)
}
extern "C" fn main() -> ! {
if cpu::online() == 0 {
SERIAL.lock().init();
}
serial!("core id: {} ({:08b})", cpu::id(), cpu::online());
let mut g = if cpu::online() == 0 {
serial!(
"code: {:x?}",
&raw const KERNEL_BEGIN..&raw const KERNEL_END
);
Window::whole().clear();
DBG.lock().clear();
KOUT.lock().clear();
println!("Hello World!");
let acpi = Acpi::load().unwrap();
serial!("global int");
#[allow(unused_variables)]
let cpus = interrupts::setup_global(acpi);
interrupts::setup_local();
unsafe { KEYBOARD.init() };
let mut g = GUARD.enter();
#[cfg(feature = "smp")]
if cpus > 1 {
serial!("boot cores");
smp::boot();
while cpu::online().count_ones() < cpus as u32 {
core::hint::spin_loop();
}
}
serial!("Loading apps");
g.scheduler.add(Thread::new(user::app_action));
g.scheduler.add(Thread::new(user::app_action));
g.scheduler.add(Thread::new(user::app_action));
g.scheduler.add(Thread::new(user::app_action));
g.scheduler.add(Thread::new(user::app_action));
g.scheduler.add(Thread::new(user::app_action));
g.scheduler.add(Thread::new(user::app_action));
g.scheduler.add(Thread::new(user::keyboard_action));
g.scheduler.add(Thread::new(user::init_action));
#[cfg(feature = "graphics")]
g.scheduler.add(Thread::new(graphics::app));
g
} else {
interrupts::setup_local();
GUARD.enter()
};
int::enable(true);
serial!("Scheduler starting...");
g.scheduler.schedule();
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
serial!(force: "{info}");
debug!("{info}");
loop {
int::enable(false);
cpu::halt();
}
}