1use core::slice;
9
10use spin::Mutex;
11
12use crate::{
13 error::KernelError,
14 graphics::{Color, PixelFormat},
15};
16
17#[repr(C, packed)]
19#[derive(Debug, Clone, Copy)]
20pub struct VbeModeInfo {
21 pub attributes: u16,
22 pub window_a: u8,
23 pub window_b: u8,
24 pub granularity: u16,
25 pub window_size: u16,
26 pub segment_a: u16,
27 pub segment_b: u16,
28 pub win_func_ptr: u32,
29 pub pitch: u16,
30 pub width: u16,
31 pub height: u16,
32 pub w_char: u8,
33 pub y_char: u8,
34 pub planes: u8,
35 pub bpp: u8,
36 pub banks: u8,
37 pub memory_model: u8,
38 pub bank_size: u8,
39 pub image_pages: u8,
40 pub reserved0: u8,
41 pub red_mask: u8,
42 pub red_position: u8,
43 pub green_mask: u8,
44 pub green_position: u8,
45 pub blue_mask: u8,
46 pub blue_position: u8,
47 pub reserved_mask: u8,
48 pub reserved_position: u8,
49 pub direct_color_attributes: u8,
50 pub framebuffer: u32,
51 pub off_screen_mem_off: u32,
52 pub off_screen_mem_size: u16,
53}
54
55#[repr(u32)]
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum GopPixelFormat {
59 RedGreenBlueReserved = 0,
60 BlueGreenRedReserved = 1,
61 BitMask = 2,
62 BltOnly = 3,
63}
64
65#[repr(C)]
67#[derive(Debug, Clone, Copy)]
68pub struct GopModeInfo {
69 pub version: u32,
70 pub horizontal_resolution: u32,
71 pub vertical_resolution: u32,
72 pub pixel_format: GopPixelFormat,
73 pub pixel_information: [u32; 4],
74 pub pixels_per_scan_line: u32,
75}
76
77pub struct GpuDriver {
79 framebuffer_addr: usize,
80 width: usize,
81 height: usize,
82 pitch: usize,
83 #[allow(dead_code)] bpp: usize,
85 pixel_format: PixelFormat,
86}
87
88impl GpuDriver {
91 pub fn from_vbe(mode_info: &VbeModeInfo) -> Result<Self, KernelError> {
93 if mode_info.framebuffer == 0 {
94 return Err(KernelError::HardwareError {
95 device: "vbe",
96 code: 1,
97 });
98 }
99
100 let pixel_format = if mode_info.red_position == 0 {
101 PixelFormat::Bgr888
102 } else {
103 PixelFormat::Rgb888
104 };
105
106 Ok(Self {
107 framebuffer_addr: mode_info.framebuffer as usize,
108 width: mode_info.width as usize,
109 height: mode_info.height as usize,
110 pitch: mode_info.pitch as usize,
111 bpp: mode_info.bpp as usize / 8,
112 pixel_format,
113 })
114 }
115
116 pub fn from_gop(framebuffer_addr: usize, mode_info: &GopModeInfo) -> Result<Self, KernelError> {
118 if framebuffer_addr == 0 {
119 return Err(KernelError::HardwareError {
120 device: "gop",
121 code: 1,
122 });
123 }
124
125 let pixel_format = match mode_info.pixel_format {
126 GopPixelFormat::RedGreenBlueReserved => PixelFormat::Rgba8888,
127 GopPixelFormat::BlueGreenRedReserved => PixelFormat::Bgra8888,
128 _ => PixelFormat::Rgba8888,
129 };
130
131 Ok(Self {
132 framebuffer_addr,
133 width: mode_info.horizontal_resolution as usize,
134 height: mode_info.vertical_resolution as usize,
135 pitch: (mode_info.pixels_per_scan_line * 4) as usize,
136 bpp: 4,
137 pixel_format,
138 })
139 }
140
141 pub fn simple(framebuffer_addr: usize, width: usize, height: usize) -> Self {
143 Self {
144 framebuffer_addr,
145 width,
146 height,
147 pitch: width * 4,
148 bpp: 4,
149 pixel_format: PixelFormat::Rgba8888,
150 }
151 }
152
153 fn framebuffer_mut(&mut self) -> &mut [u32] {
155 unsafe {
161 slice::from_raw_parts_mut(
162 self.framebuffer_addr as *mut u32,
163 (self.pitch * self.height) / 4,
164 )
165 }
166 }
167
168 pub fn set_pixel(&mut self, x: usize, y: usize, color: Color) -> Result<(), KernelError> {
170 if x >= self.width || y >= self.height {
171 return Err(KernelError::InvalidArgument {
172 name: "coordinates",
173 value: "out_of_bounds",
174 });
175 }
176
177 let offset = y * (self.pitch / 4) + x;
178 let pixel_value = self.color_to_pixel(color);
179
180 let fb = self.framebuffer_mut();
181 fb[offset] = pixel_value;
182
183 Ok(())
184 }
185
186 pub fn fill_rect(
188 &mut self,
189 x: usize,
190 y: usize,
191 w: usize,
192 h: usize,
193 color: Color,
194 ) -> Result<(), KernelError> {
195 let pixel_value = self.color_to_pixel(color);
196 let width = self.width;
197 let height = self.height;
198 let pitch = self.pitch;
199 let fb = self.framebuffer_mut();
200
201 for dy in 0..h {
202 let row_y = y + dy;
203 if row_y >= height {
204 break;
205 }
206
207 for dx in 0..w {
208 let col_x = x + dx;
209 if col_x >= width {
210 break;
211 }
212
213 let offset = row_y * (pitch / 4) + col_x;
214 fb[offset] = pixel_value;
215 }
216 }
217
218 Ok(())
219 }
220
221 pub fn clear(&mut self, color: Color) {
223 if let Err(_e) = self.fill_rect(0, 0, self.width, self.height, color) {
224 crate::println!("[GPU] Warning: fill_rect failed during clear: {:?}", _e);
225 }
226 }
227
228 fn color_to_pixel(&self, color: Color) -> u32 {
230 match self.pixel_format {
231 PixelFormat::Rgb888
232 | PixelFormat::Rgba8888
233 | PixelFormat::Xrgb8888
234 | PixelFormat::Argb8888 => {
235 ((color.a as u32) << 24)
236 | ((color.r as u32) << 16)
237 | ((color.g as u32) << 8)
238 | (color.b as u32)
239 }
240 PixelFormat::Bgr888
241 | PixelFormat::Bgra8888
242 | PixelFormat::Xbgr8888
243 | PixelFormat::Abgr8888
244 | PixelFormat::Bgrx8888 => {
245 ((color.a as u32) << 24)
246 | ((color.b as u32) << 16)
247 | ((color.g as u32) << 8)
248 | (color.r as u32)
249 }
250 PixelFormat::Rgb565 => {
251 let r5 = (color.r as u32 & 0xF8) << 8;
252 let g6 = (color.g as u32 & 0xFC) << 3;
253 let b5 = (color.b as u32) >> 3;
254 r5 | g6 | b5
255 }
256 PixelFormat::Gray8 => {
257 let luma = (color.r as u32 * 77 + color.g as u32 * 150 + color.b as u32 * 29) >> 8;
258 luma | (luma << 8) | (luma << 16) | 0xFF00_0000
259 }
260 }
261 }
262
263 pub fn width(&self) -> usize {
265 self.width
266 }
267
268 pub fn height(&self) -> usize {
270 self.height
271 }
272
273 fn detection_mode(&self) -> &'static str {
275 if self.framebuffer_addr == 0xFD000000 {
278 "fallback"
279 } else {
280 "GOP detected"
281 }
282 }
283
284 pub fn blit(
286 &mut self,
287 buffer: &[u32],
288 x: usize,
289 y: usize,
290 w: usize,
291 h: usize,
292 ) -> Result<(), KernelError> {
293 let width = self.width;
294 let height = self.height;
295 let pitch = self.pitch;
296 let fb = self.framebuffer_mut();
297
298 for dy in 0..h {
299 let row_y = y + dy;
300 if row_y >= height {
301 break;
302 }
303
304 for dx in 0..w {
305 let col_x = x + dx;
306 if col_x >= width {
307 break;
308 }
309
310 let buf_offset = dy * w + dx;
311 let fb_offset = row_y * (pitch / 4) + col_x;
312
313 if buf_offset < buffer.len() {
314 fb[fb_offset] = buffer[buf_offset];
315 }
316 }
317 }
318
319 Ok(())
320 }
321}
322
323static GPU_DRIVER: Mutex<Option<GpuDriver>> = Mutex::new(None);
325
326pub fn init() -> Result<(), KernelError> {
328 println!("[GPU] Initializing GPU driver...");
329
330 let driver = detect_framebuffer();
331
332 let (width, height, mode) = (driver.width, driver.height, driver.detection_mode());
333
334 *GPU_DRIVER.lock() = Some(driver);
335
336 println!(
337 "[GPU] GPU driver initialized ({}x{}, {})",
338 width, height, mode
339 );
340 Ok(())
341}
342
343fn detect_framebuffer() -> GpuDriver {
349 #[cfg(target_arch = "x86_64")]
350 {
351 if let Some(fb_info) = crate::arch::x86_64::boot::get_framebuffer_info() {
352 let pixel_format = if fb_info.is_bgr {
353 PixelFormat::Bgra8888
354 } else {
355 PixelFormat::Rgba8888
356 };
357
358 return GpuDriver {
359 framebuffer_addr: fb_info.buffer as usize,
360 width: fb_info.width,
361 height: fb_info.height,
362 pitch: fb_info.stride,
363 bpp: fb_info.bpp,
364 pixel_format,
365 };
366 }
367 }
368
369 GpuDriver::simple(0xFD000000, 1024, 768)
372}
373
374pub fn with_driver<R, F: FnOnce(&mut GpuDriver) -> R>(f: F) -> Option<R> {
376 GPU_DRIVER.lock().as_mut().map(f)
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
384 fn test_pixel_format() {
385 let driver = GpuDriver::simple(0x1000000, 800, 600);
386 let color = Color {
387 r: 255,
388 g: 128,
389 b: 64,
390 a: 255,
391 };
392 let pixel = driver.color_to_pixel(color);
393
394 assert_eq!(pixel, 0xFF_FF_80_40);
396 }
397}