Files
CappuccinOS/src/main.rs
2024-08-15 21:10:25 -05:00

345 lines
9.8 KiB
Rust

#![feature(abi_x86_interrupt, naked_functions, negative_impls)]
#![allow(clippy::needless_return)]
#![no_std]
#![no_main]
use core::arch::x86_64::__cpuid;
use alloc::vec::Vec;
use limine::{request::KernelFileRequest, BaseRevision};
use mem::{LabelBytes, HHDM_OFFSET, PHYSICAL_MEMORY_MANAGER};
use crate::drivers::fs::{
initramfs,
vfs::{vfs_open, UserCred},
};
extern crate alloc;
pub mod arch;
pub mod drivers;
pub mod libs;
pub mod mem;
// the build id will be an md5sum of the kernel binary and will replace __BUILD_ID__ in the final binary
pub static BUILD_ID: &str = "__BUILD_ID__";
pub static LOG_LEVEL: u8 = if cfg!(debug_assertions) { 1 } else { 2 };
// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
#[used]
// The .requests section allows limine to find the requests faster and more safely.
#[link_section = ".requests"]
static BASE_REVISION: BaseRevision = BaseRevision::new();
#[used]
#[link_section = ".requests"]
pub static KERNEL_REQUEST: KernelFileRequest = KernelFileRequest::new();
#[no_mangle]
pub extern "C" fn _start() -> ! {
drivers::serial::init_serial();
arch::gdt::gdt_init();
arch::interrupts::idt_init();
arch::interrupts::exceptions::exceptions_init();
arch::interrupts::enable_interrupts();
// TODO: memory stuff
mem::pmm::pmm_init();
mem::init_allocator();
drivers::acpi::init_acpi();
parse_kernel_cmdline();
kmain()
}
pub fn kmain() -> ! {
print_boot_info();
let _ = drivers::fs::vfs::add_vfs("/", alloc::boxed::Box::new(initramfs::init()));
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
drivers::pci::enumerate_pci_bus();
crate::println!(
"YEAH.TXT: {:X?}",
vfs_open("/firstdir/seconddirbutlonger/yeah.txt")
.unwrap()
.open(0, UserCred { uid: 0, gid: 0 })
.read(0, 0, 0)
);
drivers::storage::ide::init();
let limine_dir = vfs_open("/mnt/boot/limine").unwrap();
crate::println!(
"LIMINE BOOT: {:X?}",
limine_dir
.lookup("limine.conf")
.unwrap()
.open(0, UserCred { uid: 0, gid: 0 })
.read(0, 0, 0)
);
let root_dir = vfs_open("/").unwrap();
crate::println!(
"LIMINE BOOT THROUGH LOOKUP: {:X?}",
root_dir
.lookup("mnt")
.unwrap()
.lookup("boot")
.unwrap()
.lookup("limine")
.unwrap()
.lookup("limine.conf")
.unwrap()
.open(0, UserCred { uid: 0, gid: 0 })
.read(0, 10, 0)
);
let _ = drivers::fs::vfs::del_vfs("/mnt");
let limine_dir = vfs_open("/mnt/boot/limine").unwrap();
crate::println!(
"LIMINE BOOT: {:X?}",
limine_dir
.lookup("limine.conf")
.unwrap()
.open(0, UserCred { uid: 0, gid: 0 })
.read(0, 0, 0)
);
// let file = vfs_open("/example.txt").unwrap();
// as a sign that we didnt panic
draw_gradient();
// loop {
// let ch = crate::drivers::serial::read_serial();
// if ch == b'\x00' {
// continue;
// }
// if ch == b'\x08' {
// crate::drivers::serial::write_serial(b'\x08');
// crate::drivers::serial::write_serial(b' ');
// crate::drivers::serial::write_serial(b'\x08');
// }
// if ch > 0x1F && ch < 0x7F {
// crate::drivers::serial::write_serial(ch);
// }
// }
hcf();
}
fn draw_gradient() {
let fb = drivers::video::get_framebuffer().unwrap();
let length = (fb.height * fb.width) * (fb.bpp / 8);
let pages = length / crate::mem::pmm::PAGE_SIZE;
let buffer_ptr =
(crate::mem::PHYSICAL_MEMORY_MANAGER.alloc(pages) as usize + *HHDM_OFFSET) as *mut u8;
if buffer_ptr.is_null() {
panic!("Failed to allocate screen buffer")
}
let buffer = unsafe { core::slice::from_raw_parts_mut(buffer_ptr.cast::<u32>(), length) };
for y in 0..fb.height {
for x in 0..fb.width {
let r = (255 * x) / (fb.width - 1);
let g = (255 * y) / (fb.height - 1);
let b = 255 - r;
let pixel = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
buffer[((y * fb.pitch) / (fb.bpp / 8)) + x] = pixel
}
}
fb.blit_screen(buffer, None);
crate::mem::PHYSICAL_MEMORY_MANAGER
.dealloc((buffer_ptr as usize - *HHDM_OFFSET) as *mut u8, pages);
}
fn print_boot_info() {
// I dont really like this 'a'
crate::println!("╔═╗───────────────────╔╗────╔═╗╔══╗");
crate::println!("║╔╝╔═╗ ╔═╗╔═╗╔╦╗╔═╗╔═╗╠╣╔═╦╗║║║║══╣");
crate::println!("║╚╗║╬╚╗║╬║║╬║║║║║═╣║═╣║║║║║║║║║╠══║");
crate::println!("╚═╝╚══╝║╔╝║╔╝╚═╝╚═╝╚═╝╚╝╚╩═╝╚═╝╚══╝");
crate::println!("───────╚╝─╚╝ ©juls0730 {BUILD_ID}");
crate::println!(
"{} of memory available",
PHYSICAL_MEMORY_MANAGER.total_memory().label_bytes()
);
crate::println!(
"The kernel was built in {} mode",
if cfg!(debug_assertions) {
"debug"
} else {
"release"
}
);
if unsafe { __cpuid(0x80000000).eax } >= 0x80000004 {
let processor_brand = get_processor_brand();
crate::println!("Detected CPU: {processor_brand}");
}
}
fn get_processor_brand() -> alloc::string::String {
let mut brand_buf = [0u8; 48];
let mut offset = 0;
for i in 0..=2 {
let cpuid_result = unsafe { __cpuid(0x80000002 + i) };
brand_buf[offset..offset + 4].copy_from_slice(&cpuid_result.eax.to_le_bytes());
brand_buf[(offset + 4)..(offset + 8)].copy_from_slice(&cpuid_result.ebx.to_le_bytes());
brand_buf[(offset + 8)..(offset + 12)].copy_from_slice(&cpuid_result.ecx.to_le_bytes());
brand_buf[(offset + 12)..(offset + 16)].copy_from_slice(&cpuid_result.edx.to_le_bytes());
offset += 16;
}
// there's probably a better way to do this, but wikipedia says to not rely on the null byte, so I cant use Cstr (and I dont really want to tbh) but if it's shorter than 48bytes it will be null terminated
let mut brand = alloc::string::String::new();
for char in brand_buf {
if char == 0 {
break;
}
brand.push(char as char);
}
brand
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", &alloc::format!($($arg)*)));
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => (
$crate::drivers::serial::write_string(&alloc::format!($($arg)*).replace('\n', "\n\r"))
)
}
#[repr(u8)]
enum LogLevel {
Trace = 0,
Debug,
Info,
Warn,
Error,
Fatal,
}
#[macro_export]
macro_rules! log {
($level:expr, $($arg:tt)*) => {{
let kernel_log_level = if let Ok(kernel_features) = $crate::KERNEL_FEATURES.get() {
kernel_features.log_level
} else {
$crate::LOG_LEVEL
};
if ($level as u8) >= kernel_log_level {
let color_code = match $level {
$crate::LogLevel::Trace => "\x1B[90m",
$crate::LogLevel::Debug => "\x1B[94m",
$crate::LogLevel::Info => "\x1B[92m",
$crate::LogLevel::Warn => "\x1B[93m",
$crate::LogLevel::Error => "\x1B[91m",
$crate::LogLevel::Fatal => "\x1B[95m",
};
$crate::println!("\x1B[97m[ {}* \x1B[97m]\x1B[0;m {}", color_code, &alloc::format!($($arg)*))
}
}};
}
#[derive(Debug)]
pub struct KernelFeatures {
pub log_level: u8,
pub fat_in_mem: bool,
}
impl KernelFeatures {
fn update_option(&mut self, option: &str, value: &str) {
#[allow(clippy::single_match)]
match option {
"log_level" => self.log_level = value.parse().unwrap_or(crate::LOG_LEVEL),
"fat_in_mem" => self.fat_in_mem = value == "true",
_ => {}
}
}
}
// TODO: Do this vastly differently
pub static KERNEL_FEATURES: libs::cell::OnceCell<KernelFeatures> = libs::cell::OnceCell::new();
fn parse_kernel_cmdline() {
let mut kernel_features: KernelFeatures = KernelFeatures {
fat_in_mem: true,
log_level: crate::LOG_LEVEL,
};
let kernel_file_response = KERNEL_REQUEST.get_response();
if kernel_file_response.is_none() {
KERNEL_FEATURES.set(kernel_features);
return;
}
let cmdline = core::str::from_utf8(kernel_file_response.unwrap().file().cmdline());
let kernel_arguments = cmdline.unwrap().split_whitespace().collect::<Vec<&str>>();
// crate::log!(LogLevel::Trace, "{kernel_arguments:?}");
for item in kernel_arguments {
let parts: Vec<&str> = item.split('=').collect();
if parts.len() == 2 {
let (option, value) = (parts[0], parts[1]);
kernel_features.update_option(option, value);
}
}
KERNEL_FEATURES.set(kernel_features);
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
crate::println!("{info}");
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
let rbp: u64;
unsafe {
core::arch::asm!("mov {0:r}, rbp", out(reg) rbp);
};
crate::arch::stack_trace::print_stack_trace(6, rbp);
}
hcf();
}
pub fn hcf() -> ! {
loop {
unsafe {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
core::arch::asm!("hlt");
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
core::arch::asm!("wfi");
}
}
}