Add simple uart driver, more functionality etc etc etc

This commit is contained in:
ivajon
2026-05-06 20:43:00 +02:00
parent 0b0acedb2f
commit aa5ce32dce
20 changed files with 1554 additions and 2 deletions

12
kernel/Cargo.lock generated
View File

@@ -5,3 +5,15 @@ version = 4
[[package]]
name = "kernel"
version = "0.1.0"
dependencies = [
"uefi",
"x86_drivers",
]
[[package]]
name = "uefi"
version = "0.1.0"
[[package]]
name = "x86_drivers"
version = "0.1.0"

View File

@@ -4,3 +4,5 @@ version = "0.1.0"
edition = "2024"
[dependencies]
uefi = {path = "../uefi"}
x86_drivers = {path = "../x86_drivers"}

10
kernel/run.sh Executable file
View File

@@ -0,0 +1,10 @@
mkdir -p esp/EFI/BOOT
cargo b --target x86_64-unknown-uefi --release
cp target/x86_64-unknown-uefi/release/kernel.efi esp/EFI/BOOT/BOOTX64.EFI
qemu-system-x86_64 \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
-drive if=pflash,format=raw,file=OVMF_VARS.4m.fd \
-drive format=raw,file=fat:rw:esp \
-net none \
-serial stdio

64
kernel/src/lib.rs Normal file
View File

@@ -0,0 +1,64 @@
#![no_std]
#![deny(clippy::perf, clippy::all, clippy::pedantic)]
use core::{
mem::MaybeUninit,
sync::atomic::{AtomicBool, Ordering},
};
use uefi::EfiSystemTable;
pub use x86_drivers::prelude::*;
use x86_drivers::uart::Uart1;
#[macro_export]
macro_rules! print {
($($tt:tt)+) => {
#[allow(static_mut_refs)]
'body: {
use core::fmt::Write as _;
if let Some(uart) = $crate::uart_is_init() {
let _ = core::write!(uart, $($tt)*);
break 'body;
}
let table = unsafe { $crate::TABLE.assume_init().as_mut().unwrap_unchecked() };
let _ = core::write!(table.std_out(), $($tt)*);
}
};
}
/// # Panics
/// Panics if the UART `MaybeUninit` pointer is null (should never happen).
#[allow(static_mut_refs)]
pub fn uart_is_init() -> Option<&'static mut Uart<Uart1>> {
unsafe {
UART.0
.load(Ordering::Relaxed)
.then_some(UART.1.as_mut_ptr().as_mut().expect("uart ptr is non-null"))
}
}
pub static mut TABLE: MaybeUninit<*mut EfiSystemTable> = MaybeUninit::uninit();
static mut UART: (AtomicBool, MaybeUninit<Uart<Uart1>>) =
(AtomicBool::new(false), MaybeUninit::uninit());
#[repr(align(8))]
pub struct Aligned<T>(pub T);
#[used]
pub static mut MEMORY_MAP_ARENA: Aligned<[u8; 32 * 1_024]> = Aligned([0; _]);
/// # Panics
/// Panics if UART has already been specified.
#[allow(static_mut_refs)]
pub fn specify_uart(uart: Uart<Uart1>) {
let is_specified = unsafe { UART.0.load(Ordering::Acquire) };
assert!(!is_specified, "Uart already specified");
unsafe { UART.1.write(uart) };
unsafe { UART.0.store(true, Ordering::Release) };
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
print!("PANIC: {:?}\r\n", info);
loop {}
}

View File

@@ -1,3 +1,109 @@
fn main() {
println!("Hello, world!");
#![no_std]
#![no_main]
#![deny(clippy::perf, clippy::all, clippy::pedantic)]
use uefi::{EfiMemoryType, EfiSystemTable, ExitBootServicesKey, Handle, UefiStatus};
use kernel::print;
use x86_drivers::gdt::{DPL, GdtEntry, GdtPointer, GdtSelector};
pub use x86_drivers::prelude::*;
#[unsafe(no_mangle)]
extern "efiapi" fn efi_main(handle: Handle, table: *mut EfiSystemTable) -> UefiStatus {
let peripherals = x86_drivers::core::Core::take();
#[allow(static_mut_refs)]
unsafe {
kernel::TABLE.write(table)
};
let table = unsafe { table.as_mut().expect("efi table ptr is non-null") };
let res = table.std_out().print("Hello world\r\n");
if let Err(Some(e)) = res {
return e;
}
kernel::specify_uart(peripherals.uart1.init());
print!("Hello from uart!\r\n");
#[allow(static_mut_refs)]
let arena = unsafe { &mut kernel::MEMORY_MAP_ARENA.0 };
let (_key, map, _version) = table
.boot_services()
.memory_map(arena)
.expect("memory map failed");
for el in map
.filter_map(|segment| segment.memory_type.interpret().map(|f| (f, segment)))
.filter(|(t, _segment)| *t == EfiMemoryType::ConventionalMemory)
{
print!("Map element {:?}\r\n", el);
}
let default = ExitBootServicesKey::default();
print!("Default {:?}", default);
let boot_services = table.boot_services();
boot_services
.exit_boot_services(handle, arena)
.expect("exit boot services failed");
print!("Exited boot services!\r\n");
print!("kernel_code: {:#018x}\r\n", GdtEntry::kernel_code().raw());
print!("kernel_data: {:#018x}\r\n", GdtEntry::kernel_data().raw());
print!(
"Initiating GDT to {:?}, CODE_SEL: {:?}, DATA SEL: {:?}",
GDT_TABLE, GDT_KERNEL_CODE_SELECTOR, GDT_KERNEL_DATA_SELECTOR
);
gdt_init();
print!("Initiated GDT");
#[allow(clippy::empty_loop)]
loop {}
}
static GDT_TABLE: [GdtEntry; 3] = [
GdtEntry::null(),
GdtEntry::kernel_code(),
GdtEntry::kernel_data(),
];
static GDT_KERNEL_CODE_SELECTOR: GdtSelector = GdtSelector::new(DPL::Kernel, &GDT_TABLE, 1);
static GDT_KERNEL_DATA_SELECTOR: GdtSelector = GdtSelector::new(DPL::Kernel, &GDT_TABLE, 2);
fn gdt_init() {
let ptr = GdtPointer::new(&GDT_TABLE);
print!("GDTR: {:?}\r\n", ptr,);
print!("GDT_TABLE addr: {:#x}\r\n", &GDT_TABLE as *const _ as u64);
unsafe {
core::arch::asm!(
"lgdt [{}]",
in(reg) &ptr,
options(readonly, nostack, preserves_flags)
);
}
print!("Passed lgdt");
unsafe {
core::arch::asm!(
"push {code_sel}",
"lea {tmp}, [rip + 2f]",
"push {tmp}",
"retfq",
"2:",
code_sel = const GDT_KERNEL_CODE_SELECTOR.raw() as u64,
tmp = out(reg) _,
);
}
print!("CS reloaded\r\n");
unsafe {
core::arch::asm!(
"mov ds, {data_sel:x}",
"mov es, {data_sel:x}",
"mov fs, {data_sel:x}",
"mov gs, {data_sel:x}",
"mov ss, {data_sel:x}",
data_sel = in(reg) GDT_KERNEL_DATA_SELECTOR.raw(),
);
}
print!("data segments reloaded\r\n");
}