kernel/graphics/
vga.rs

1use core::sync::atomic::{AtomicI64, Ordering};
2
3use embedded_graphics::{
4    draw_target::DrawTarget,
5    geometry::{self, OriginDimensions},
6    pixelcolor::Rgb888,
7};
8pub use kernel_user_link::graphics::FrameBufferInfo;
9
10use crate::{
11    memory_management::virtual_space::VirtualSpace,
12    multiboot2::{self, FramebufferColorInfo},
13    sync::{
14        once::OnceLock,
15        spin::mutex::{Mutex, MutexGuard},
16    },
17};
18
19use super::Pixel;
20
21static VGA_DISPLAY_CONTROLLER: OnceLock<VgaDisplayController> = OnceLock::new();
22
23pub fn init(framebuffer: Option<multiboot2::Framebuffer>) {
24    if VGA_DISPLAY_CONTROLLER.try_get().is_some() {
25        panic!("VGA display controller already initialized");
26    }
27
28    match framebuffer {
29        Some(framebuffer) => match framebuffer.color_info {
30            FramebufferColorInfo::Indexed { .. } => {}
31            FramebufferColorInfo::Rgb { .. } => {
32                // only initialize if the framebuffer is RGB
33                VGA_DISPLAY_CONTROLLER.get_or_init(|| VgaDisplayController::new(framebuffer));
34            }
35            FramebufferColorInfo::EgaText => {}
36        },
37        None => panic!("No framebuffer provided"),
38    }
39}
40
41pub fn controller() -> Option<&'static VgaDisplayController> {
42    VGA_DISPLAY_CONTROLLER.try_get()
43}
44
45pub struct VgaDisplayController {
46    display: Mutex<VgaDisplay>,
47    framebuffer_info: FrameBufferInfo,
48    owner_process: AtomicI64,
49}
50
51#[allow(dead_code)]
52impl VgaDisplayController {
53    pub fn new(framebuffer: multiboot2::Framebuffer) -> Self {
54        let display = VgaDisplay::new(framebuffer);
55
56        Self {
57            framebuffer_info: display.fb_info,
58            display: Mutex::new(display),
59            owner_process: AtomicI64::new(-1),
60        }
61    }
62
63    pub fn lock_process(&self, pid: u64) -> Option<MutexGuard<'_, VgaDisplay>> {
64        if self.owner_process.load(Ordering::Relaxed) == pid as i64 {
65            Some(self.display.lock())
66        } else {
67            None
68        }
69    }
70
71    pub fn lock_kernel(&self) -> Option<MutexGuard<'_, VgaDisplay>> {
72        if self.owner_process.load(Ordering::Relaxed) == -1 {
73            Some(self.display.lock())
74        } else {
75            None
76        }
77    }
78
79    pub fn take_ownership(&self, pid: u64) -> bool {
80        assert!(pid < i64::MAX as u64);
81        self.owner_process
82            .compare_exchange(
83                -1,
84                pid as i64,
85                core::sync::atomic::Ordering::Relaxed,
86                Ordering::Relaxed,
87            )
88            .is_ok()
89    }
90
91    pub fn release(&self, pid: u64) -> bool {
92        assert!(pid < i64::MAX as u64);
93        self.owner_process
94            .compare_exchange(
95                pid as i64,
96                -1,
97                core::sync::atomic::Ordering::Relaxed,
98                Ordering::Relaxed,
99            )
100            .is_ok()
101    }
102
103    pub fn framebuffer_info(&self) -> &FrameBufferInfo {
104        &self.framebuffer_info
105    }
106}
107
108// extra custom functionality
109trait FrameBufferDraw {
110    fn read_pixel(&self, memory: &[u8], pos: (usize, usize)) -> Option<Pixel>;
111    fn write_pixel(&self, memory: &mut [u8], pos: (usize, usize), color: Pixel) -> Option<()>;
112}
113
114impl FrameBufferDraw for FrameBufferInfo {
115    fn read_pixel(&self, memory: &[u8], pos: (usize, usize)) -> Option<Pixel> {
116        let pixel_mem = self.pixel_mem(memory, pos)?;
117        Some(Pixel {
118            r: pixel_mem[self.field_pos.0 as usize],
119            g: pixel_mem[self.field_pos.1 as usize],
120            b: pixel_mem[self.field_pos.2 as usize],
121        })
122    }
123
124    fn write_pixel(&self, memory: &mut [u8], pos: (usize, usize), color: Pixel) -> Option<()> {
125        let pixel_mem = self.pixel_mem_mut(memory, pos)?;
126        let r = color.r & self.mask.0;
127        let g = color.g & self.mask.1;
128        let b = color.b & self.mask.2;
129        pixel_mem[self.field_pos.0 as usize] = r;
130        pixel_mem[self.field_pos.1 as usize] = g;
131        pixel_mem[self.field_pos.2 as usize] = b;
132
133        Some(())
134    }
135}
136
137pub struct VgaDisplay {
138    fb_info: FrameBufferInfo,
139    memory: VirtualSpace<[u8]>,
140}
141
142#[allow(dead_code)]
143impl VgaDisplay {
144    fn new(framebuffer: multiboot2::Framebuffer) -> Self {
145        let multiboot2::FramebufferColorInfo::Rgb {
146            red_field_position,
147            red_mask_size,
148            green_field_position,
149            green_mask_size,
150            blue_field_position,
151            blue_mask_size,
152        } = framebuffer.color_info
153        else {
154            panic!("Only RGB framebuffer is supported");
155        };
156        assert_eq!(framebuffer.bpp % 8, 0, "Only byte aligned bpp is supported");
157        assert!(
158            red_field_position.is_multiple_of(8)
159                && green_field_position.is_multiple_of(8)
160                && blue_field_position.is_multiple_of(8),
161            "Only byte aligned field position is supported"
162        );
163
164        let physical_addr = framebuffer.addr;
165        let memory_size = framebuffer.pitch * framebuffer.height;
166        let memory =
167            unsafe { VirtualSpace::new_slice(physical_addr, memory_size as usize).unwrap() };
168
169        let red_mask = (1 << red_mask_size) - 1;
170        let green_mask = (1 << green_mask_size) - 1;
171        let blue_mask = (1 << blue_mask_size) - 1;
172        Self {
173            fb_info: FrameBufferInfo {
174                pitch: framebuffer.pitch as usize,
175                height: framebuffer.height as usize,
176                width: framebuffer.width as usize,
177                field_pos: (
178                    red_field_position / 8,
179                    green_field_position / 8,
180                    blue_field_position / 8,
181                ),
182                mask: (red_mask as u8, green_mask as u8, blue_mask as u8),
183                byte_per_pixel: framebuffer.bpp.div_ceil(8),
184            },
185            memory,
186        }
187    }
188
189    pub fn put_pixel(&mut self, x: usize, y: usize, color: Pixel) {
190        self.fb_info.write_pixel(&mut self.memory, (x, y), color);
191    }
192
193    pub fn clear(&mut self) {
194        self.memory.fill(0);
195    }
196
197    pub fn blit_inner_ranges(
198        &mut self,
199        src: (usize, usize),
200        dest: (usize, usize),
201        width: usize,
202        height: usize,
203    ) {
204        let (src_x, src_y) = src;
205        let (dest_x, dest_y) = dest;
206        assert!(src_x + width <= self.fb_info.width);
207        assert!(src_y + height <= self.fb_info.height);
208        assert!(dest_x + width <= self.fb_info.width);
209        assert!(dest_y + height <= self.fb_info.height);
210
211        // assert no overlap
212        assert!(
213            src_x + width <= dest_x
214                || dest_x + width <= src_x
215                || src_y + height <= dest_y
216                || dest_y + height <= src_y
217        );
218
219        let chunk_size = width * self.fb_info.byte_per_pixel as usize;
220
221        // Some optimization to avoid checking the condition for each pixel
222        // we create a closure that will be called for each line
223        let is_src_after =
224            src_y * self.fb_info.pitch + src_x > dest_y * self.fb_info.pitch + dest_x;
225        let mut copy_handler_src_after;
226        let mut copy_handler_src_before;
227        let copy_handler: &mut dyn FnMut(usize, usize, &mut [u8]) = if is_src_after {
228            copy_handler_src_after = |src_i: usize, dest_i: usize, memory: &mut [u8]| {
229                let (before, after) = memory.split_at_mut(src_i);
230                before[dest_i..dest_i + chunk_size].copy_from_slice(&after[..chunk_size]);
231            };
232            &mut copy_handler_src_after
233        } else {
234            copy_handler_src_before = |src_i: usize, dest_i: usize, memory: &mut [u8]| {
235                let (before, after) = memory.split_at_mut(src_i);
236                before[dest_i..dest_i + chunk_size].copy_from_slice(&after[..chunk_size]);
237            };
238            &mut copy_handler_src_before
239        };
240
241        for y in 0..height {
242            let src_i = self.fb_info.get_arr_pos((src_x, src_y + y)).unwrap();
243            let dest_i = self.fb_info.get_arr_pos((dest_x, dest_y + y)).unwrap();
244
245            copy_handler(src_i, dest_i, &mut self.memory);
246        }
247    }
248
249    pub fn blit(
250        &mut self,
251        src_buffer: &[u8],
252        src_framebuffer_info: &FrameBufferInfo,
253        src: (usize, usize),
254        dest: (usize, usize),
255        width: usize,
256        height: usize,
257    ) {
258        if self.fb_info.byte_per_pixel == src_framebuffer_info.byte_per_pixel
259            && self.fb_info.field_pos == src_framebuffer_info.field_pos
260            && self.fb_info.mask == src_framebuffer_info.mask
261        {
262            // Safety: we know this can work because we have the same format
263            unsafe { self.blit_fast(src_buffer, src_framebuffer_info, src, dest, width, height) }
264        } else {
265            self.blit_slow(src_buffer, src_framebuffer_info, src, dest, width, height)
266        }
267    }
268
269    /// blit the src framebuffer to the current framebuffer
270    /// `fast` here means that we assume the src and dest have the same format
271    unsafe fn blit_fast(
272        &mut self,
273        src_buffer: &[u8],
274        src_framebuffer_info: &FrameBufferInfo,
275        src: (usize, usize),
276        dest: (usize, usize),
277        width: usize,
278        height: usize,
279    ) {
280        let (src_x, src_y) = src;
281        let (dest_x, dest_y) = dest;
282        assert!(src_x + width <= src_framebuffer_info.width);
283        assert!(src_y + height <= src_framebuffer_info.height);
284        assert!(dest_x + width <= self.fb_info.width);
285        assert!(dest_y + height <= self.fb_info.height);
286
287        // assume same chunk size
288        let chunk_size = width * self.fb_info.byte_per_pixel as usize;
289
290        for y in 0..height {
291            let src_i = src_framebuffer_info
292                .get_arr_pos((src_x, src_y + y))
293                .unwrap();
294            let dest_i = self.fb_info.get_arr_pos((dest_x, dest_y + y)).unwrap();
295
296            let src_line = &src_buffer[src_i..src_i + chunk_size];
297            let dest_line = &mut self.memory[dest_i..dest_i + chunk_size];
298            dest_line.copy_from_slice(src_line);
299        }
300    }
301
302    fn blit_slow(
303        &mut self,
304        src_buffer: &[u8],
305        src_framebuffer_info: &FrameBufferInfo,
306        src: (usize, usize),
307        dest: (usize, usize),
308        width: usize,
309        height: usize,
310    ) {
311        let (src_x, src_y) = src;
312        let (dest_x, dest_y) = dest;
313
314        assert!(dest_x + width <= self.fb_info.width);
315        assert!(dest_y + height <= self.fb_info.height);
316        assert!(src_x + width <= src_framebuffer_info.width);
317        assert!(src_y + height <= src_framebuffer_info.height);
318
319        for y in 0..height {
320            for x in 0..width {
321                let src_pixel = src_framebuffer_info
322                    .read_pixel(src_buffer, (src_x + x, src_y + y))
323                    .unwrap();
324                self.fb_info
325                    .write_pixel(&mut self.memory, (dest_x + x, dest_y + y), src_pixel)
326                    .unwrap();
327            }
328        }
329    }
330
331    pub fn clear_rect(
332        &mut self,
333        dest_x: usize,
334        dest_y: usize,
335        width: usize,
336        height: usize,
337        color: Pixel,
338    ) {
339        assert!(dest_x + width <= self.fb_info.width);
340        assert!(dest_y + height <= self.fb_info.height);
341
342        if height == 0 || width == 0 {
343            return;
344        }
345
346        let line_chunk_size = width * self.fb_info.byte_per_pixel as usize;
347        let first_line_start = self.fb_info.get_arr_pos((dest_x, dest_y)).unwrap();
348        let first_line_end = first_line_start + line_chunk_size;
349        let first_line = &mut self.memory[first_line_start..first_line_end];
350
351        // fill the first line
352        for i in 0..width {
353            self.fb_info.write_pixel(first_line, (i, 0), color);
354        }
355
356        // take from the end of the first line, i.e. `before` will have the first line
357        // and `after` will have the rest of the memory
358        let second_line_start = self.fb_info.get_arr_pos((0, dest_y + 1)).unwrap();
359        let (before, after) = self.memory.split_at_mut(second_line_start);
360        let first_line = &before[first_line_start..first_line_end];
361
362        for y in 1..height {
363            let dest_i = self.fb_info.get_arr_pos((dest_x, y - 1)).unwrap();
364            let dest_line = &mut after[dest_i..dest_i + line_chunk_size];
365            dest_line.copy_from_slice(first_line);
366        }
367    }
368}
369
370impl DrawTarget for VgaDisplay {
371    type Color = Rgb888;
372
373    type Error = ();
374
375    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
376    where
377        I: IntoIterator<Item = embedded_graphics::prelude::Pixel<Self::Color>>,
378    {
379        for embedded_graphics::prelude::Pixel(pos, color) in pixels {
380            self.put_pixel(pos.x as usize, pos.y as usize, color.into());
381        }
382        Ok(())
383    }
384
385    fn fill_solid(
386        &mut self,
387        area: &embedded_graphics::primitives::Rectangle,
388        color: Self::Color,
389    ) -> Result<(), Self::Error> {
390        if area.top_left.x < 0
391            || area.top_left.y < 0
392            || area.bottom_right().unwrap().x >= self.fb_info.width as i32
393            || area.bottom_right().unwrap().y >= self.fb_info.height as i32
394        {
395            return Err(());
396        }
397
398        let (x, y) = (area.top_left.x as usize, area.top_left.y as usize);
399        let (width, height) = (area.size.width as usize, area.size.height as usize);
400        self.clear_rect(x, y, width, height, color.into());
401
402        Ok(())
403    }
404
405    fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
406        self.clear_rect(0, 0, self.fb_info.width, self.fb_info.height, color.into());
407        Ok(())
408    }
409}
410
411impl OriginDimensions for VgaDisplay {
412    fn size(&self) -> geometry::Size {
413        geometry::Size::new(self.fb_info.width as u32, self.fb_info.height as u32)
414    }
415}