mycelium_kernel/arch/x86_64/
framebuf.rs

1use bootloader_api::{info, BootInfo};
2use core::{
3    mem,
4    ops::{Deref, DerefMut},
5};
6use hal_x86_64::framebuffer::{self, Framebuffer};
7use mycelium_util::sync::{
8    blocking::{Mutex, MutexGuard},
9    spin::Spinlock,
10    InitOnce,
11};
12
13#[derive(Debug)]
14pub struct FramebufGuard(MutexGuard<'static, info::FrameBuffer, Spinlock>);
15pub type FramebufWriter = Framebuffer<'static, FramebufGuard>;
16
17/// Locks the framebuffer and returns a [`FramebufWriter`].
18///
19/// # Safety
20///
21/// In release mode, this function *assumes* the frame buffer has been
22/// initialized by [`init`]. If this is ever called before [`init`] has been
23/// called and returned `true`, this may read uninitialized memory!
24pub(super) unsafe fn mk_framebuf() -> FramebufWriter {
25    let (cfg, buf) = unsafe {
26        // Safety: we can reasonably assume this will only be called
27        // after `arch_entry`, so if we've failed to initialize the
28        // framebuffer...things have gone horribly wrong...
29        FRAMEBUFFER.get_unchecked()
30    };
31    Framebuffer::new(cfg, FramebufGuard(buf.lock()))
32}
33
34/// Forcibly unlock the framebuffer mutex.
35///
36/// # Safety
37///
38/// This forcibly unlocks a potentially-locked mutex, violating mutual
39/// exclusion! This should only be called in conditions where no other CPU core
40/// will *ever* attempt to access the framebuffer again (such as while oopsing).
41pub(super) unsafe fn force_unlock() {
42    if let Some((_, fb)) = FRAMEBUFFER.try_get() {
43        fb.force_unlock();
44    }
45}
46
47/// Try to initialize the framebuffer based on the provided [`BootInfo`].
48///
49/// Returns `true` if the framebuffer is available, or `false` if there is no
50/// framebuffer enabled.
51///
52/// If the framebuffer has already been initialized, this does nothing.
53pub(super) fn init(bootinfo: &mut BootInfo) -> bool {
54    use info::Optional;
55    // Has the framebuffer already been initialized?
56    if FRAMEBUFFER.try_get().is_some() {
57        return true;
58    }
59
60    // Okay, try to initialize the framebuffer
61    let Optional::Some(framebuffer) = mem::replace(&mut bootinfo.framebuffer, Optional::None)
62    else {
63        // The boot info does not contain a framebuffer configuration. Nothing
64        // for us to do!
65        return false;
66    };
67
68    let info = framebuffer.info();
69    let cfg = framebuffer::Config {
70        height: info.height,
71        width: info.width,
72        px_bytes: info.bytes_per_pixel,
73        line_len: info.stride,
74        px_kind: match info.pixel_format {
75            info::PixelFormat::Rgb => framebuffer::PixelKind::Rgb,
76            info::PixelFormat::Bgr => framebuffer::PixelKind::Bgr,
77            info::PixelFormat::U8 => framebuffer::PixelKind::Gray,
78            x => unimplemented!("hahaha wtf, found a weird pixel format: {:?}", x),
79        },
80    };
81    FRAMEBUFFER.init((cfg, Mutex::new_with_raw_mutex(framebuffer, Spinlock::new())));
82    true
83}
84
85static FRAMEBUFFER: InitOnce<(framebuffer::Config, Mutex<info::FrameBuffer, Spinlock>)> =
86    InitOnce::uninitialized();
87
88impl Deref for FramebufGuard {
89    type Target = [u8];
90
91    #[inline(always)]
92    fn deref(&self) -> &Self::Target {
93        self.0.buffer()
94    }
95}
96
97impl DerefMut for FramebufGuard {
98    #[inline(always)]
99    fn deref_mut(&mut self) -> &mut Self::Target {
100        self.0.buffer_mut()
101    }
102}