//! A minimal freestanding x86_64 kernel. //! //! Boot flow: BIOS -> `bootloader` crate (switches to 64-bit long mode, sets up //! paging) -> `_start` below. There is no OS underneath us, so no `std`, no //! `main`, and no heap. Everything we print goes straight to hardware. #![no_std] // don't link the Rust standard library (it needs an OS) #![no_main] // no normal `main`: the bootloader jumps to `_start` mod serial; // COM1 serial port -> shows up in your terminal via `-serial stdio` mod vga_buffer; // 80x25 VGA text mode screen at physical address 0xb8000 // The bootloader is linked in by `cargo bootimage`, not called from our code; // this line just tells Rust/cargo the dependency is intentional. extern crate bootloader; use core::panic::PanicInfo; /// Kernel entry point. The bootloader calls this after setting up the CPU. /// `no_mangle` keeps the symbol name `_start` so the linker can find it. #[unsafe(no_mangle)] pub extern "C" fn _start() -> ! { println!("Hello from Tyler's Rust kernel!"); println!("VGA text mode is working. Next stop: interrupts."); serial_println!("Hello from Tyler's Rust kernel! (via serial COM1)"); serial_println!("Kernel booted successfully."); // In headless mode, power QEMU off so scripts/CI finish on their own. #[cfg(feature = "headless")] exit_qemu(QemuExitCode::Success); hlt_loop(); } /// Called on `panic!`. Print the message to both outputs, then stop. #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); serial_println!("KERNEL PANIC: {}", info); #[cfg(feature = "headless")] exit_qemu(QemuExitCode::Failed); hlt_loop(); } /// Sleep the CPU until the next interrupt, forever (cheaper than `loop {}`). pub fn hlt_loop() -> ! { loop { x86_64::instructions::hlt(); } } /// Exit codes for QEMU's `isa-debug-exit` device. QEMU exits with /// status `(value << 1) | 1`, so Success = 0x10 -> shell exit code 33. #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum QemuExitCode { Success = 0x10, Failed = 0x11, } /// Write to I/O port 0xf4 (where we put `isa-debug-exit`) to quit QEMU. #[allow(dead_code)] pub fn exit_qemu(exit_code: QemuExitCode) { use x86_64::instructions::port::Port; unsafe { let mut port = Port::new(0xf4); port.write(exit_code as u32); } }