345 lines
9.8 KiB
Rust
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");
|
|
}
|
|
}
|
|
}
|