Skip to main content

roxy_loader_api/
framebuffer.rs

1use uefi::proto::console::gop::PixelFormat as UefiPixelFormat;
2
3/// A framebuffer provided to the kernel at boot time.
4///
5/// Kernels can use this value to find the framebuffer memory and understand its
6/// basic layout.
7#[derive(Clone, Copy)]
8#[repr(C)]
9pub struct Framebuffer {
10    ptr: usize,
11    /// The total size of the framebuffer in bytes.
12    pub size: usize,
13    /// The number of pixels in each row of the framebuffer.
14    pub stride: usize,
15    /// Pixel format of the framebuffer.
16    pub pixel_format: PixelFormat,
17    /// Width of the framebuffer in pixels.
18    pub width: usize,
19    /// Height of the framebuffer in pixels.
20    pub height: usize,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24#[repr(C)]
25pub enum PixelFormat {
26    Rgb,
27    Bgr,
28    Bitmask,
29    BltOnly,
30}
31
32impl From<UefiPixelFormat> for PixelFormat {
33    fn from(value: UefiPixelFormat) -> Self {
34        match value {
35            UefiPixelFormat::Rgb => Self::Rgb,
36            UefiPixelFormat::Bgr => Self::Bgr,
37            UefiPixelFormat::Bitmask => Self::Bitmask,
38            UefiPixelFormat::BltOnly => Self::BltOnly,
39        }
40    }
41}
42
43type Resolution = (usize, usize);
44
45impl Framebuffer {
46    /// Bytes each pixel will take on the framebuffer.
47    pub fn bytes_per_pixel(&self) -> usize {
48        self.size / (self.stride * self.height)
49    }
50
51    /// Creates a framebuffer value.
52    pub fn new(
53        ptr: *mut u8,
54        size: usize,
55        stride: usize,
56        pixel_format: impl Into<PixelFormat>,
57        resolution: Resolution,
58    ) -> Self {
59        let (width, height) = resolution;
60
61        Self {
62            ptr: ptr as usize,
63            size,
64            stride,
65            width,
66            height,
67            pixel_format: pixel_format.into(),
68        }
69    }
70
71    /// Returns a pointer to the framebuffer memory.
72    pub fn ptr(&self) -> *mut u8 {
73        self.ptr as *mut u8
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::{Framebuffer, PixelFormat};
80    use core::{
81        mem::{MaybeUninit, align_of, size_of},
82        ptr::addr_of,
83    };
84    use uefi::proto::console::gop::PixelFormat as UefiPixelFormat;
85
86    #[test]
87    fn layout_is_stable() {
88        assert_eq!(size_of::<Framebuffer>(), 48);
89        assert_eq!(align_of::<Framebuffer>(), align_of::<usize>());
90        assert_eq!(size_of::<PixelFormat>(), 4);
91        assert_eq!(align_of::<PixelFormat>(), 4);
92
93        let framebuffer = MaybeUninit::<Framebuffer>::uninit();
94        let base = framebuffer.as_ptr();
95
96        // ABI stability matters because the loader and kernel share this struct across a raw boundary.
97        unsafe {
98            assert_eq!(addr_of!((*base).ptr) as usize - base as usize, 0);
99            assert_eq!(
100                addr_of!((*base).size) as usize - base as usize,
101                size_of::<usize>()
102            );
103            assert_eq!(
104                addr_of!((*base).stride) as usize - base as usize,
105                size_of::<usize>() * 2
106            );
107            assert_eq!(
108                addr_of!((*base).pixel_format) as usize - base as usize,
109                size_of::<usize>() * 3
110            );
111            assert_eq!(
112                addr_of!((*base).width) as usize - base as usize,
113                size_of::<usize>() * 4
114            );
115            assert_eq!(
116                addr_of!((*base).height) as usize - base as usize,
117                size_of::<usize>() * 5
118            );
119        }
120    }
121
122    #[test]
123    fn new_preserves_framebuffer_metadata() {
124        let ptr = 0x1000 as *mut u8;
125        let framebuffer = Framebuffer::new(ptr, 800 * 600 * 4, 800, PixelFormat::Bgr, (800, 600));
126
127        assert_eq!(framebuffer.ptr(), ptr);
128        assert_eq!(framebuffer.size, 800 * 600 * 4);
129        assert_eq!(framebuffer.stride, 800);
130        assert_eq!(framebuffer.pixel_format, PixelFormat::Bgr);
131        assert_eq!(framebuffer.width, 800);
132        assert_eq!(framebuffer.height, 600);
133    }
134
135    #[test]
136    fn bytes_per_pixel_uses_stride_height_and_size() {
137        let framebuffer = Framebuffer::new(
138            core::ptr::null_mut(),
139            1024 * 768 * 4,
140            1024,
141            PixelFormat::Rgb,
142            (800, 768),
143        );
144
145        assert_eq!(framebuffer.bytes_per_pixel(), 4);
146    }
147
148    #[test]
149    fn converts_uefi_pixel_formats() {
150        assert_eq!(PixelFormat::from(UefiPixelFormat::Rgb), PixelFormat::Rgb);
151        assert_eq!(PixelFormat::from(UefiPixelFormat::Bgr), PixelFormat::Bgr);
152        assert_eq!(
153            PixelFormat::from(UefiPixelFormat::Bitmask),
154            PixelFormat::Bitmask
155        );
156        assert_eq!(
157            PixelFormat::from(UefiPixelFormat::BltOnly),
158            PixelFormat::BltOnly
159        );
160    }
161}