From cffad21a64641ab96ff896b039444baccdf5c8d4 Mon Sep 17 00:00:00 2001 From: janis Date: Thu, 16 Jul 2026 16:27:37 +0200 Subject: [PATCH] draw to framebuffer --- kernel/.cargo/config.toml | 1 + kernel/build.rs | 6 ++ kernel/kernel.lds | 43 ++++++++ kernel/src/lib.rs | 2 + kernel/src/limine.rs | 164 ++++++++++++++++++++++++++++++ kernel/src/main.rs | 43 ++++++++ kernel/x86_64-unknown-kernel.json | 53 +++++----- 7 files changed, 286 insertions(+), 26 deletions(-) create mode 100644 kernel/build.rs create mode 100644 kernel/kernel.lds create mode 100644 kernel/src/limine.rs diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index 954c12e..c3220c8 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -3,6 +3,7 @@ codegen-backend = "llvm" [unstable] json-target-spec = true # lets us specify a custom target specification file +build-std-features = ["compiler-builtins-mem"] build-std = ["core", "compiler_builtins"] [build] diff --git a/kernel/build.rs b/kernel/build.rs new file mode 100644 index 0000000..373fd84 --- /dev/null +++ b/kernel/build.rs @@ -0,0 +1,6 @@ +fn main() { + println!("cargo::rerun-if-changed=build.rs"); + + println!("cargo::rustc-link-arg-bin=kernel=-Tkernel.lds"); + println!("cargo::rerun-if-changed=kernel.lds"); +} diff --git a/kernel/kernel.lds b/kernel/kernel.lds new file mode 100644 index 0000000..c3318ef --- /dev/null +++ b/kernel/kernel.lds @@ -0,0 +1,43 @@ +OUTPUT_FORMAT(elf64-x86-64) +ENTRY(_start) + +PHDRS { + limine_requests PT_LOAD; + text PT_LOAD; + rodata PT_LOAD; + data PT_LOAD; +} + +SECTIONS { + . = 0xffffffff80000000; + .limine_requests : { + KEEP(*(.limine_requests_start)) + KEEP(*(.limine_requests)) + KEEP(*(.limine_requests_end)) + } :limine_requests + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + .text : { + *(.text .text.*) + } :text + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + .rodata : { + *(.rodata .rodata.*) + } :rodata + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + .data : { + *(.data .data.*) + } :data + + .bss : { + *(.bss .bss.*) + *(COMMON) + } :data + + /DISCARD/ : { + *(.eh_frame*) + *(.note .note.*) + } +} diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 7206481..7f32173 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +#![feature(const_trait_impl, const_default)] +pub mod limine; pub mod x86_64; /// # Safety diff --git a/kernel/src/limine.rs b/kernel/src/limine.rs new file mode 100644 index 0000000..e669d77 --- /dev/null +++ b/kernel/src/limine.rs @@ -0,0 +1,164 @@ +use core::{cell::UnsafeCell, ptr::NonNull}; + +#[repr(C)] +pub struct BaseRevision(UnsafeCell<[u64; 3]>); + +unsafe impl Send for BaseRevision {} +unsafe impl Sync for BaseRevision {} + +impl BaseRevision { + pub const fn from_revision(revision: u64) -> Self { + Self(UnsafeCell::new([ + 0xf9562b2d5c95a6c8, + 0x6a7b384944536bdc, + revision, + ])) + } + pub fn is_supported(&self) -> bool { + unsafe { self.0.get().cast::().add(2).read_volatile() == 0 } + } + pub fn base_revision(&self) -> u64 { + unsafe { self.0.get().cast::().add(2).read_volatile() } + } +} + +#[repr(transparent)] +pub struct RequestsStartMarker([u64; 4]); +pub const REQUESTS_START_MARKER: RequestsStartMarker = RequestsStartMarker([ + 0xf6b8f4b39de7d1ae, + 0xfab91a6940fcb9cf, + 0x785c6ed015d3e316, + 0x181e920a7852b9d9, +]); + +#[repr(transparent)] +pub struct RequestsEndMarker([u64; 2]); +pub const REQUESTS_END_MARKER: RequestsEndMarker = + RequestsEndMarker([0xadc0e0531bb10d03, 0x9572709f31764c62]); + +#[repr(C)] +pub struct Request { + magic: [u64; 2], + id: [u64; 2], + revision: u64, + response: UnsafeCell>>>, + request: U, +} + +unsafe impl Send for Request {} +unsafe impl Sync for Request {} + +#[repr(C)] +pub struct Response { + revision: u64, + response: T, +} + +impl Request { + pub const MAGIC: [u64; 2] = [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b]; + + pub const fn new_raw(id: [u64; 2], revision: u64, request: U) -> Self { + Self { + magic: Self::MAGIC, + id, + revision, + response: UnsafeCell::new(None), + request, + } + } + + pub fn response(&self) -> Option<&Response> { + unsafe { + self.response + .get() + .cast::>>>() + .read_volatile() + } + .map(|ptr| unsafe { ptr.as_ref() }) + } +} + +#[repr(C)] +pub struct Framebuffer { + pub addr: *mut u8, + pub width: u64, + pub height: u64, + pub pitch: u64, + pub bpp: u16, + pub memory_model: u8, + pub red_mask_size: u8, + pub red_mask_shift: u8, + pub green_mask_size: u8, + pub green_mask_shift: u8, + pub blue_mask_size: u8, + pub blue_mask_shift: u8, + pub reserved: [u8; 7], + pub edid_size: u64, + pub edid: *mut u8, +} + +pub struct Color(pub [u8; 3]); + +impl Framebuffer { + pub fn draw_pixel(&self, x: u64, y: u64, color: Color) { + assert!(self.bpp <= 64); + let bytes_per_pixel = self.bpp.div_ceil(8) as usize; + + let [r, g, b] = color.0; + + let mask_color = |c: u8, bits: u8, shift: u8| { + let mask = (1 << bits) - 1; + (c as u64 & mask) << shift + }; + + let mut pixel = 0u64; + pixel |= mask_color(r, self.red_mask_size, self.red_mask_shift); + pixel |= mask_color(g, self.green_mask_size, self.green_mask_shift); + pixel |= mask_color(b, self.blue_mask_size, self.blue_mask_shift); + + let pixel = pixel.to_le_bytes(); + + let dst = unsafe { + self.addr + .add((y * self.pitch + x * bytes_per_pixel as u64) as usize) + }; + unsafe { super::volatile_copy(pixel.as_ptr(), dst, bytes_per_pixel) } + } +} + +#[repr(C)] +pub struct FramebufferResponse { + count: u64, + framebuffers: *const *const Framebuffer, +} + +pub type FramebufferRequest = Request; + +const impl Default for FramebufferRequest { + fn default() -> Self { + Self::new() + } +} + +impl FramebufferRequest { + pub const ID: [u64; 2] = [0x9d5827dcd881dd75, 0xa3148604f6fab11b]; + + pub const fn new() -> Self { + Self::new_raw(Self::ID, 0, ()) + } + + pub fn framebuffers<'limine>(&self) -> &'limine [&'limine Framebuffer] { + // SAFETY: limine responses are guaranteed to be valid for the lifetime of the memory mapping. + self.response() + .map(|response| unsafe { + core::slice::from_raw_parts( + response + .response + .framebuffers + .cast::<&'limine Framebuffer>(), + response.response.count as usize, + ) + }) + .unwrap_or(&[]) + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 734bd41..e6c3a01 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -8,5 +8,48 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { #[unsafe(no_mangle)] fn _start() -> ! { + assert!(limine_requests::LIMINE_BASE_REVISION.is_supported()); + + let fb = limine_requests::FRAMEBUFFER_REQUEST + .framebuffers() + .first() + .expect("No framebuffer found"); + + for y in 0..fb.height { + for x in 0..fb.width { + let mut bits = ((x * 255 / fb.width) as u32) << 8; + bits ^= (y * 255 / fb.height) as u32; + + fb.draw_pixel( + x, + y, + kernel::limine::Color(*bits.to_le_bytes().first_chunk().unwrap()), + ); + } + } + kernel::x86_64::halt_loop() } + +mod limine_requests { + use kernel::limine::{ + BaseRevision, FramebufferRequest, REQUESTS_END_MARKER, REQUESTS_START_MARKER, + RequestsEndMarker, RequestsStartMarker, + }; + + #[unsafe(link_section = ".limine_requests_start")] + #[unsafe(no_mangle)] + static LIMINE_REQUESTS_START: RequestsStartMarker = REQUESTS_START_MARKER; + + #[unsafe(link_section = ".limine_requests")] + #[unsafe(no_mangle)] + pub static LIMINE_BASE_REVISION: BaseRevision = BaseRevision::from_revision(6); + + #[unsafe(link_section = ".limine_requests")] + #[unsafe(no_mangle)] + pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); + + #[unsafe(link_section = ".limine_requests_end")] + #[unsafe(no_mangle)] + static LIMINE_REQUESTS_END: RequestsEndMarker = REQUESTS_END_MARKER; +} diff --git a/kernel/x86_64-unknown-kernel.json b/kernel/x86_64-unknown-kernel.json index 66061b6..18c2d50 100644 --- a/kernel/x86_64-unknown-kernel.json +++ b/kernel/x86_64-unknown-kernel.json @@ -1,28 +1,29 @@ { - "arch": "x86_64", - "code-model": "kernel", - "cpu": "x86-64", - "crt-objects-fallback": "false", - "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", - "disable-redzone": true, - "target-endian": "little", - "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float", - "linker": "mold", - "linker-flavor": "gnu", - "llvm-target": "x86_64-unknown-none-elf", - "max-atomic-width": 64, - "panic-strategy": "abort", - "plt-by-default": false, - "position-independent-executables": true, - "relro-level": "full", - "rustc-abi": "softfloat", - "stack-probes": { - "kind": "inline" - }, - "static-position-independent-executables": true, - "supported-sanitizers": [ - "kcfi", - "kernel-address" - ], - "target-pointer-width": 64 + "arch": "x86_64", + "code-model": "kernel", + "cpu": "x86-64", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "disable-redzone": true, + "target-endian": "little", + "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "llvm-target": "x86_64-unknown-none-elf", + "max-atomic-width": 64, + "panic-strategy": "abort", + "plt-by-default": false, + "os": "none", + "executables": true, + "relro-level": "full", + "rustc-abi": "softfloat", + "stack-probes": { + "kind": "inline" + }, + "supported-sanitizers": [ + "kcfi", + "kernel-address" + ], + "target-pointer-width": 64, + "target-c-int-width": 32 }