// Copyright (c) 2025 shibedrill // SPDX-License-Identifier: MIT #![no_std] #![no_main] #![feature(abi_x86_interrupt)] mod arch; mod boot; mod constants; mod interrupt; #[macro_use] mod log; mod memory; mod panic; mod process; mod util; use core::ptr::addr_of; use arch::x86_64::interrupts::IDT; use arch::x86_64::serial::SerialPort; use boot::{BASE_REVISION, params, *}; use constants::*; use log::*; use memory::alloc::{boxed::Box, format, vec}; use params::*; use lazy_static::lazy_static; use limine::firmware::{ FIRMWARE_TYPE_EFI32, FIRMWARE_TYPE_EFI64, FIRMWARE_TYPE_SBI, FIRMWARE_TYPE_X86BIOS, }; use spin::mutex::Mutex; use x86_64::{VirtAddr, registers::rflags::RFlags, structures::idt::InterruptStackFrameValue}; use crate::{ arch::{ asm::ltr, x86_64::{ cpuid::{CPUID, virt_supported}, gdt::SELECTORS, }, }, boot::modules::MODULE_RESPONSE, memory::paging::active_root_table, }; lazy_static! { pub static ref SERIAL_3F8: Mutex = Mutex::new( arch::x86_64::serial::SerialPort::try_from_port(0x3f8).expect("Could not create port") ); } #[unsafe(no_mangle)] unsafe extern "C" fn main() -> ! { // CRITICAL AREA // Nothing non-essential should be done until we can initialize logging // Otherwise it will fail silently // Assert supported bootloader version assert!(BASE_REVISION.is_supported()); // Set up logging level from params // Nothing we can do here if this fails since no log subscribers are initialized yet if let Some(level) = PARAMS.get("-loglevel") && let Ok(parsed_level) = LogLevel::try_from(level.as_str()) { LOGGER.lock().level = parsed_level; } // Add subscribers to logger if let Some(device) = PARAMS.get("-logdev") { let log_device_list: vec::Vec<&str> = device.split(',').collect(); if log_device_list.contains(&"display") { // Append display console to log subs } if log_device_list.contains(&"serial") { // TODO: Set up device discovery let serial_logger = |msg: &str| SERIAL_3F8.lock().log_writeln(msg); LOGGER.lock().add_device(Box::new(serial_logger)); } log_trace!("Configured kernel logging devices"); } // END OF CRITICAL AREA // Fallible code can be placed below this comment // Initialize all statics so the bootloader memory can be reclaimed memory::init_statics(); // Ensure IDT exists SELECTORS.gdt.load(); log_info!("GDT loaded!"); IDT.load(); log_info!("IDT loaded!"); log_info!( "Kernel cmdline: {}", EXECUTABLE_FILE_RESPONSE.executable_file().cmdline() ); log_info!( "Kernel file path: {}", EXECUTABLE_FILE_RESPONSE.executable_file().path() ); let log_level = log::LOGGER.lock().level; log_info!("Log level: {:?}", log_level); // Branding for line in ASCII_LOGO { let mut locked = SERIAL_3F8.lock(); locked.log_write("\t\t"); locked.log_writeln(line); } SERIAL_3F8 .lock() .log_writeln("\tWelcome to the Gila microkernel!\n"); log_info!("Booting gila version {}", kernel_version_string()); match boot::FIRMWARE_TYPE_REQUEST.response() { Some(resp) => log_info!( "Firmware type: {}", match resp.firmware_type { FIRMWARE_TYPE_SBI => "SBI", FIRMWARE_TYPE_EFI32 => "UEFI (32-bit)", FIRMWARE_TYPE_EFI64 => "UEFI (64-bit)", FIRMWARE_TYPE_X86BIOS => "x86 BIOS", _ => "Unknown", } ), None => log_warning!("Firmware type: No response"), } log_info!("Trans rights!"); if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.response() { let fb = framebuffer_response.framebuffers_rev1().unwrap()[0]; log_info!("Framebuffer response received"); for mode in fb.modes() { log_trace!( "Mode: {}x{}, {} bits per pixel", mode.width, mode.height, mode.bpp ); } } else { log_info!("Framebuffer response absent, graphics disabled",); } let smp_response = MP_REQUEST.response(); match smp_response { None => log_info!("Multiprocessing response not received"), Some(resp) => { log_info!("Multiprocessing response received"); log_info!("{} CPUs found", resp.cpus().len()); } } let rsdp_response = RSDP_REQUEST.response(); match rsdp_response { None => log_info!("RDSP response not received"), Some(resp) => { log_info!("RSDP response received"); log_trace!("RSDP address: 0x{:x}", resp.address as usize) } } boot::modules::log_modules(); memory::log_memory(); memory::log_address(); if let Some(string) = CPUID.get_processor_brand_string() { log_info!("CPU brand string: {}", string.as_str()); } if let Some(string) = CPUID.get_vendor_info() { log_info!("CPU vendor: {}", string.as_str()); } if let Some(string) = CPUID.get_hypervisor_info() { log_info!("Hypervisor: {:?}", string.identify()); } log_info!("Virtualization provider: {:?}", virt_supported()); { let root_ptable_info = unsafe { active_root_table() }; log_info!( "Root page table is Level {} at 0x{:x}", root_ptable_info.1, addr_of!(*root_ptable_info.0) as usize ); // This is an expensive function. Only run it if we're actually logging the results. //let loglevel = LOGGER.lock().level; //if loglevel <= LogLevel::Trace { // paging::walk_tables(); //} } if let Some(modules_list) = *MODULE_RESPONSE && modules_list.modules()[0].path().ends_with("/userboot.bin") { let ub = modules_list.modules()[0]; log_info!("Found userboot: {} {}", ub.path(), ub.cmdline()); //let start_addr = VirtAddr::from_ptr(ub.data() as *const [u8]); let start_addr = VirtAddr::from_ptr(usermode_fn as *const fn()); log_info!("Start address: 0x{:x}", start_addr); x86_64::registers::model_specific::Star::write( SELECTORS.sel_user_code, SELECTORS.sel_user_stack, SELECTORS.sel_kernel_code, SELECTORS.sel_kernel_stack, ) .expect("fuck"); ltr(SELECTORS.sel_tss.index()); unsafe { InterruptStackFrameValue::new( VirtAddr::from_ptr(usermode_fn as *const fn()), SELECTORS.sel_user_code, RFlags::INTERRUPT_FLAG, VirtAddr::new(0xffffffff80000000), SELECTORS.sel_user_stack, ) .iretq(); } } // TODO: // - Map APIC // - Create an interrupt stack frame, push it to stack, and iret to userboot panic!("Finished boot, but cannot start init because processes not implemented!"); } fn usermode_fn() -> ! { log_info!("In usermode!"); loop { arch::asm::nop(); } }