From d027b01f3b843f60b3b24cc9a94d6cc21260a581 Mon Sep 17 00:00:00 2001 From: Zhe Tang Date: Sat, 22 Nov 2025 14:34:09 +0000 Subject: [PATCH] Implement the `ioctl` handlers for getting and setting information for the EFI framebuffer. --- kernel/src/device/fb.rs | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/kernel/src/device/fb.rs b/kernel/src/device/fb.rs index 257e04c37..510031a47 100644 --- a/kernel/src/device/fb.rs +++ b/kernel/src/device/fb.rs @@ -223,6 +223,86 @@ impl CharDevice for Fb { } } +impl FbHandle { + /// Reads an array of `u16` color map values from userspace. + fn read_color_maps_from_user(addr: usize, data: &mut [u16]) -> Result<()> { + for (i, item) in data.iter_mut().enumerate() { + let user_addr = addr + i * size_of::(); + *item = current_userspace!().read_val(user_addr)?; + } + Ok(()) + } + + /// Writes an array of `u16` color map values to userspace. + fn write_color_maps_to_user(addr: usize, data: &[u16]) -> Result<()> { + for (i, &value) in data.iter().enumerate() { + let user_addr = addr + i * size_of::(); + current_userspace!().write_val(user_addr, &value)?; + } + Ok(()) + } + + /// Handles the [`IoctlCmd::GETVSCREENINFO`] ioctl command. + /// + /// Arguments: + /// - Input: None. + /// - Output: [`FbVarScreenInfo`]. + fn handle_get_var_screen_info(&self, arg: usize) -> Result { + /// Default pixel clock calculation for efifb compatibility + const DEFAULT_PIXEL_CLOCK_DIVISOR: u32 = 10_000_000; + + /// Default timing parameters for efifb compatibility + const DEFAULT_RIGHT_MARGIN: u32 = 32; + const DEFAULT_UPPER_MARGIN: u32 = 16; + const DEFAULT_LOWER_MARGIN: u32 = 4; + const DEFAULT_VSYNC_LEN: u32 = 4; + + let pixel_format = self.framebuffer.pixel_format(); + let (red, green, blue, transp) = FbBitfield::from_pixel_format(pixel_format); + + let screen_info = FbVarScreenInfo { + xres: self.framebuffer.width() as u32, + yres: self.framebuffer.height() as u32, + xres_virtual: self.framebuffer.width() as u32, + yres_virtual: self.framebuffer.height() as u32, + bits_per_pixel: (8 * pixel_format.nbytes()) as u32, + red, + green, + blue, + transp, + pixclock: DEFAULT_PIXEL_CLOCK_DIVISOR / self.framebuffer.width() as u32 * 1000 + / self.framebuffer.height() as u32, + left_margin: (self.framebuffer.width() as u32 / 8) & 0xf8, + right_margin: DEFAULT_RIGHT_MARGIN, + upper_margin: DEFAULT_UPPER_MARGIN, + lower_margin: DEFAULT_LOWER_MARGIN, + vsync_len: DEFAULT_VSYNC_LEN, + hsync_len: (self.framebuffer.width() as u32 / 8) & 0xf8, + ..Default::default() + }; + + current_userspace!().write_val(arg, &screen_info)?; + Ok(0) + } + + /// Handles the [`IoctlCmd::GETFSCREENINFO`] ioctl command. + /// + /// Arguments: + /// - Input: None. + /// - Output: [`FbFixScreenInfo`]. + fn handle_get_fix_screen_info(&self, arg: usize) -> Result { + let screen_info = FbFixScreenInfo { + smem_start: self.framebuffer.io_mem().paddr() as u64, + smem_len: self.framebuffer.io_mem().size() as u32, + line_length: self.framebuffer.line_size() as u32, + ..Default::default() + }; + + current_userspace!().write_val(arg, &screen_info)?; + Ok(0) + } +} + impl Pollable for FbHandle { fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents { let events = IoEvents::IN | IoEvents::OUT; @@ -304,6 +384,14 @@ impl FileIo for FbHandle { fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { match cmd { + IoctlCmd::GETVSCREENINFO => self.handle_get_var_screen_info(arg), + IoctlCmd::GETFSCREENINFO => self.handle_get_fix_screen_info(arg), + IoctlCmd::PUTVSCREENINFO => { + // EFI framebuffers do not support changing settings. Linux + // will return the old settings to user space and succeed. + // Reference: . + self.handle_get_var_screen_info(arg) + } _ => { log::debug!( "the ioctl command {:?} is not supported by framebuffer devices",