From 8c09e046e9af52f656d8f0b77f0d108b87680c76 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sun, 8 Dec 2024 14:49:29 +0200 Subject: [PATCH] video: x86_64 switch-over from boot fb to virtio-gpu if present --- boot/yboot-proto/src/v1.rs | 5 + boot/yboot/src/main.rs | 13 +- kernel/driver/block/nvme/src/queue.rs | 6 +- kernel/driver/fs/kernel-fs/src/devfs.rs | 27 +++- kernel/driver/virtio/gpu/src/command.rs | 1 - kernel/driver/virtio/gpu/src/lib.rs | 23 ++-- kernel/libk/src/device/display.rs | 98 +++++++++++++++ kernel/libk/src/vfs/block/cache.rs | 2 +- kernel/src/arch/x86_64/boot/mod.rs | 2 + kernel/src/arch/x86_64/mod.rs | 117 +++++++++-------- kernel/src/debug.rs | 17 +-- kernel/src/device/display/console.rs | 32 ++--- kernel/src/device/display/device.rs | 73 ++++++----- kernel/src/device/display/fb_console.rs | 161 ++++++++++++------------ kernel/src/device/display/linear_fb.rs | 25 ++-- kernel/src/device/display/mod.rs | 10 -- userspace/arch/aarch64/rc.d/99-colors | 2 +- userspace/arch/x86_64/rc.d/99-colors | 2 +- userspace/colors/src/display.rs | 16 +-- userspace/colors/src/main.rs | 16 +-- 20 files changed, 400 insertions(+), 248 deletions(-) diff --git a/boot/yboot-proto/src/v1.rs b/boot/yboot-proto/src/v1.rs index 5bf1290b..e0f39773 100644 --- a/boot/yboot-proto/src/v1.rs +++ b/boot/yboot-proto/src/v1.rs @@ -4,6 +4,9 @@ use bytemuck::{Pod, Zeroable}; use crate::{AvailableRegion, IterableMemoryMap, LoadProtocolHeader}; +pub const PIXEL_R8G8B8A8: u32 = 1; +pub const PIXEL_B8G8R8A8: u32 = 2; + #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct LoadProtocol { @@ -46,6 +49,8 @@ pub struct FramebufferOption { pub res_stride: u64, pub res_address: u64, pub res_size: u64, + pub res_format: u32, + pub _0: u32, } impl AvailableRegion for AvailableMemoryRegion { diff --git a/boot/yboot/src/main.rs b/boot/yboot/src/main.rs index 92833ad8..88c2ed6d 100644 --- a/boot/yboot/src/main.rs +++ b/boot/yboot/src/main.rs @@ -14,7 +14,9 @@ use log::{debug, error, info}; use uefi::{ prelude::*, proto::{ - console::gop::GraphicsOutput, device_path::DevicePath, loaded_image::LoadedImage, + console::gop::{GraphicsOutput, PixelFormat}, + device_path::DevicePath, + loaded_image::LoadedImage, media::fs::SimpleFileSystem, }, table::{ @@ -24,7 +26,7 @@ use uefi::{ Error, }; use yboot_proto::{ - v1::{AvailableMemoryRegion, FramebufferOption}, + v1::{self, AvailableMemoryRegion, FramebufferOption}, LoadProtocolV1, LOADER_MAGIC, }; @@ -47,11 +49,18 @@ fn setup_framebuffer(bs: &BootServices, fb: &mut FramebufferOption) -> Result<() let mut result = gop.frame_buffer(); + let format = match mode.info().pixel_format() { + PixelFormat::Bgr => v1::PIXEL_B8G8R8A8, + PixelFormat::Rgb => v1::PIXEL_R8G8B8A8, + _ => 0, + }; + fb.res_width = fb.req_width; fb.res_height = fb.req_height; fb.res_address = result.as_mut_ptr() as _; fb.res_stride = mode.info().stride() as u64 * 4; fb.res_size = result.size() as _; + fb.res_format = format; info!( "Framebuffer: {}x{} @ {:#x}", diff --git a/kernel/driver/block/nvme/src/queue.rs b/kernel/driver/block/nvme/src/queue.rs index 1fe2a4f5..8e12480b 100644 --- a/kernel/driver/block/nvme/src/queue.rs +++ b/kernel/driver/block/nvme/src/queue.rs @@ -1,9 +1,6 @@ use core::{future::poll_fn, mem::size_of, ptr::null_mut, task::Poll}; -use alloc::{ - collections::{BTreeMap, BTreeSet}, - vec::Vec, -}; +use alloc::collections::{BTreeMap, BTreeSet}; use bytemuck::{Pod, Zeroable}; use libk_mm::{ address::{AsPhysicalAddress, PhysicalAddress}, @@ -96,6 +93,7 @@ pub struct QueuePair { pub struct PrpList { prp1: PhysicalRegionPage, prp2: PhysicalRegionPage, + #[allow(unused)] list: Option>, } diff --git a/kernel/driver/fs/kernel-fs/src/devfs.rs b/kernel/driver/fs/kernel-fs/src/devfs.rs index b0ff34e5..f07e132f 100644 --- a/kernel/driver/fs/kernel-fs/src/devfs.rs +++ b/kernel/driver/fs/kernel-fs/src/devfs.rs @@ -2,7 +2,11 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use alloc::{format, string::String}; -use libk::vfs::{block::BlockDevice, impls::MemoryDirectory, CharDevice, Node, NodeFlags, NodeRef}; +use libk::vfs::{ + block::BlockDevice, + impls::{fixed_symlink, MemoryDirectory}, + AccessToken, CharDevice, Node, NodeFlags, NodeRef, +}; use libk_util::OneTimeInit; use yggdrasil_abi::error::Error; @@ -39,6 +43,16 @@ pub fn root() -> &'static NodeRef { DEVFS_ROOT.get() } +pub fn set_node(name: &str, node: NodeRef) -> Result<(), Error> { + log::info!("Update node: {name}"); + + let root = DEVFS_ROOT.get(); + root.remove_file(name, unsafe { AccessToken::authorized() }) + .ok(); + + root.add_child(name, fixed_symlink(node)) +} + /// Adds a character device with a custom name pub fn add_named_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Error> { log::info!("Add char device: {}", name); @@ -52,16 +66,21 @@ pub fn add_named_char_device(dev: &'static dyn CharDevice, name: String) -> Resu pub fn add_named_block_device>( dev: &'static dyn BlockDevice, name: S, -) -> Result<(), Error> { +) -> Result { let name = name.into(); log::info!("Add block device: {}", name); let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS); - DEVFS_ROOT.get().add_child(name, node) + DEVFS_ROOT.get().add_child(name, node.clone())?; + + Ok(node) } -pub fn add_block_device(dev: &'static dyn BlockDevice, kind: BlockDeviceType) -> Result<(), Error> { +pub fn add_block_device( + dev: &'static dyn BlockDevice, + kind: BlockDeviceType, +) -> Result { static FB_COUNT: AtomicUsize = AtomicUsize::new(0); let (count, prefix) = match kind { diff --git a/kernel/driver/virtio/gpu/src/command.rs b/kernel/driver/virtio/gpu/src/command.rs index bb979c0b..86ca898c 100644 --- a/kernel/driver/virtio/gpu/src/command.rs +++ b/kernel/driver/virtio/gpu/src/command.rs @@ -1,4 +1,3 @@ -use alloc::vec::Vec; use bytemuck::{Pod, Zeroable}; use libk::{device::display::PixelFormat, error::Error}; use libk_mm::{address::PhysicalAddress, PageBox}; diff --git a/kernel/driver/virtio/gpu/src/lib.rs b/kernel/driver/virtio/gpu/src/lib.rs index e3dff74d..417b3557 100644 --- a/kernel/driver/virtio/gpu/src/lib.rs +++ b/kernel/driver/virtio/gpu/src/lib.rs @@ -5,16 +5,14 @@ extern crate alloc; use core::mem::MaybeUninit; use alloc::{boxed::Box, vec::Vec}; -use bytemuck::{Pod, Zeroable}; -use command::{ControlLock, Rect, ScanoutInfo}; +use command::{ControlLock, ScanoutInfo}; use device_api::Device; use libk::device::display::{ register_display_device, DisplayDevice, DisplayDeviceWrapper, DisplayMode, DisplayOwner, - FramebufferInfo, PixelFormat, + DriverFlags, FramebufferInfo, PixelFormat, }; use libk_mm::{ address::{PhysicalAddress, Virtualize}, - device::RawDeviceMemoryMapping, phys, table::MapAttributes, PageBox, PageProvider, L3_PAGE_SIZE, @@ -59,6 +57,7 @@ struct Config { pub struct VirtioGpu { transport: IrqSafeSpinlock, + #[allow(unused)] pci_device_info: Option, queues: OneTimeInit, @@ -296,19 +295,23 @@ impl PageProvider for VirtioGpu { fn clone_page( &self, - offset: u64, - src_phys: PhysicalAddress, - src_attrs: MapAttributes, + _offset: u64, + _src_phys: PhysicalAddress, + _src_attrs: MapAttributes, ) -> Result { todo!() } - fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> { + fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> { Ok(()) } } impl DisplayDevice for VirtioGpu { + fn driver_flags(&self) -> DriverFlags { + DriverFlags::REPLACES_BOOT_FRAMEBUFFER + } + fn lock(&self, owner: DisplayOwner) -> Result<(), Error> { let mut config = self.config.write(); if config.owner == owner { @@ -341,7 +344,7 @@ impl DisplayDevice for VirtioGpu { self.flush() } - fn set_display_mode(&self, index: u32, double_buffer: bool) -> Result<(), Error> { + fn set_display_mode(&self, _index: u32, _double_buffer: bool) -> Result<(), Error> { todo!() } @@ -383,7 +386,7 @@ impl DisplayDevice for VirtioGpu { }) } - fn query_display_modes(&self, modes: &mut [MaybeUninit]) -> Result { + fn query_display_modes(&self, _modes: &mut [MaybeUninit]) -> Result { todo!() } } diff --git a/kernel/libk/src/device/display.rs b/kernel/libk/src/device/display.rs index a2b625e2..02d5029c 100644 --- a/kernel/libk/src/device/display.rs +++ b/kernel/libk/src/device/display.rs @@ -10,6 +10,7 @@ use device_api::Device; use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider}; use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit}; use yggdrasil_abi::{ + bitflags, error::Error, io::{DeviceRequest, Framebuffer}, process::ProcessId, @@ -17,6 +18,22 @@ use yggdrasil_abi::{ use crate::{task::thread::Thread, vfs::block::BlockDevice}; +bitflags! { + /// Init-time display driver flags + pub struct DriverFlags: u32 { + /// If set, replaces a previosly present boot framebuffer if one was present + const REPLACES_BOOT_FRAMEBUFFER: bit 0; + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + /// Represents the current lock holder for a display device #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DisplayOwner { @@ -81,6 +98,9 @@ pub struct FramebufferInfo { /// Abstract display device interface pub trait DisplayDevice: Device + PageProvider { + /// Returns the init-time driver flags + fn driver_flags(&self) -> DriverFlags; + /// Returns the active display mode fn active_display_mode(&self) -> Option; /// Returns the list of available display modes @@ -221,3 +241,81 @@ impl Deref for DisplayDeviceWrapper { self.inner } } + +const _0: () = const { + let c1 = Color::from_rgba_u32(0x11223344); + let c2 = Color::from_rgb_u32(0x112233); + if c1.r != 0x11 || c1.g != 0x22 || c1.b != 0x33 || c1.a != 0x44 { + panic!(); + } + if c2.r != 0x11 || c2.g != 0x22 || c2.b != 0x33 { + panic!(); + } + // if color.to_le_rgba_u32() != 0x11223344 { + // panic!(); + // } + // assert_eq!(color.to_rgba_u32(), 0x11223344); +}; + +impl Color { + pub const BLACK: Self = Self::from_rgb_u32(0x000000); + pub const WHITE: Self = Self::from_rgb_u32(0xFFFFFF); + pub const RED: Self = Self::from_rgb_u32(0xFF0000); + + #[inline] + pub const fn from_rgba_u32(v: u32) -> Self { + // 0x11223344 -> [0x44, 0x33, 0x22, 0x11] + // a b g r + let x = v.to_le_bytes(); + Self { + a: x[0], + b: x[1], + g: x[2], + r: x[3], + } + } + + #[inline] + pub const fn from_rgb_u32(v: u32) -> Self { + let x = v.to_le_bytes(); + Self { + a: u8::MAX, + b: x[0], + g: x[1], + r: x[2], + } + } + + #[inline] + pub const fn to_rgba_u32(self) -> u32 { + u32::from_ne_bytes([self.r, self.g, self.b, self.a]) + } + + #[inline] + pub const fn to_bgra_u32(self) -> u32 { + u32::from_ne_bytes([self.b, self.g, self.r, self.a]) + } + + #[inline] + pub const fn to_pixel_u32(self, format: PixelFormat) -> u32 { + match format { + PixelFormat::B8G8R8A8 => self.to_bgra_u32(), + PixelFormat::R8G8B8A8 => self.to_rgba_u32(), + _ => unimplemented!(), + } + } + + pub const fn rgb_mul(self, v: u8) -> Self { + Self { + a: self.a, + r: self.r.saturating_mul(v), + g: self.g.saturating_mul(v), + b: self.b.saturating_mul(v), + } + } + // pub fn rgb_mul_assign(&mut self, v: u8) { + // self.r = self.r.saturating_mul(v); + // self.g = self.g.saturating_mul(v); + // self.b = self.b.saturating_mul(v); + // } +} diff --git a/kernel/libk/src/vfs/block/cache.rs b/kernel/libk/src/vfs/block/cache.rs index 79cfa549..ead31bc9 100644 --- a/kernel/libk/src/vfs/block/cache.rs +++ b/kernel/libk/src/vfs/block/cache.rs @@ -282,7 +282,7 @@ impl> BlockCache { pub async fn try_with_mut Result>( &self, block_position: u64, - size: usize, + _size: usize, mapper: F, ) -> Result { let segment_position = block_position & !(self.segment_size as u64 - 1); diff --git a/kernel/src/arch/x86_64/boot/mod.rs b/kernel/src/arch/x86_64/boot/mod.rs index b1ac0022..ffeaf634 100644 --- a/kernel/src/arch/x86_64/boot/mod.rs +++ b/kernel/src/arch/x86_64/boot/mod.rs @@ -55,6 +55,8 @@ static YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 { res_stride: 0, res_address: 0, res_size: 0, + res_format: 0, + _0: 0, }, }; diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 086fea00..185b2867 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -3,7 +3,7 @@ use core::{ mem::size_of, ops::{Deref, DerefMut}, ptr::null_mut, - sync::atomic::{AtomicU32, Ordering}, + sync::atomic::Ordering, }; use ::acpi::{mcfg::Mcfg, AcpiTables, InterruptModel}; @@ -29,6 +29,7 @@ use libk::{ device::{ display::{ register_display_device, set_display_registration_callback, DisplayDeviceWrapper, + DriverFlags, PixelFormat, }, register_external_interrupt_controller, }, @@ -40,7 +41,10 @@ use libk_mm::{ table::{EntryLevel, EntryLevelExt}, }; use libk_util::{sync::SpinFence, OneTimeInit}; -use yboot_proto::{v1::AvailableMemoryRegion, LoadProtocolV1}; +use yboot_proto::{ + v1::{self, AvailableMemoryRegion}, + LoadProtocolV1, +}; use ygg_driver_pci::PciBusManager; mod acpi; @@ -55,7 +59,7 @@ use crate::{ debug::{self, LogLevel}, device::{ self, - display::{console, fb_console::FramebufferConsole, linear_fb::LinearFramebuffer}, + display::{fb_console::FramebufferConsole, linear_fb::LinearFramebuffer}, }, fs::{Initrd, INITRD_DATA}, }; @@ -348,7 +352,26 @@ impl X86_64 { set_display_registration_callback(|device| { log::info!("Display registered: {:?}", device.display_name()); - devfs::add_block_device(device, BlockDeviceType::Framebuffer).ok(); + let node = devfs::add_block_device(device, BlockDeviceType::Framebuffer); + + if device + .driver_flags() + .contains(DriverFlags::REPLACES_BOOT_FRAMEBUFFER) + { + // Switch fbconsole to the new framebuffer + if let Some(fbconsole) = PLATFORM.fbconsole.try_get() + && fbconsole.uses_boot_framebuffer() + { + if let Err(error) = fbconsole.set_display(device.deref()) { + log::error!("Couldn't set fb console display: {error:?}"); + } + } + + // Switch /dev/fb0 to the new node + if let Ok(node) = node { + devfs::set_node("fb0", node).ok(); + } + } }); self.init_framebuffer()?; @@ -425,53 +448,47 @@ impl X86_64 { } unsafe fn init_framebuffer(&'static self) -> Result<(), Error> { - // TODO use boot fb temporarily and then switch to whatever proper GPU is detected + let display = match self.boot_data.get() { + &BootData::YBoot(data) => { + let info = &data.opt_framebuffer; + let format = match info.res_format { + v1::PIXEL_B8G8R8A8 => PixelFormat::B8G8R8A8, + v1::PIXEL_R8G8B8A8 => PixelFormat::R8G8B8A8, + // Fallback + _ => PixelFormat::R8G8B8A8, + }; + + let framebuffer = LinearFramebuffer::from_physical_bits( + PhysicalAddress::from_u64(info.res_address), + info.res_size as usize, + info.res_stride as usize, + info.res_width, + info.res_height, + format, + )?; + let framebuffer = self.gop_framebuffer.init(framebuffer); + + Some(framebuffer) + } + }; + + let Some(display) = display else { + return Ok(()); + }; + + // Register boot display + let display = Box::leak(Box::new(DisplayDeviceWrapper::new(display))); + register_display_device(display); + + let fbconsole = self.fbconsole.init(FramebufferConsole::from_framebuffer( + (*display).deref(), + None, + true, + )?); + + debug::add_sink(fbconsole, LogLevel::Info); + Ok(()) - // let display = match self.boot_data.get() { - // &BootData::YBoot(data) => { - // let info = &data.opt_framebuffer; - - // let framebuffer = LinearFramebuffer::from_physical_bits( - // PhysicalAddress::from_u64(info.res_address), - // info.res_size as usize, - // info.res_stride as usize, - // info.res_width, - // info.res_height, - // )?; - // let framebuffer = self.gop_framebuffer.init(framebuffer); - - // Some(framebuffer) - // } - // }; - - // let Some(display) = display else { - // return Ok(()); - // }; - - // // Register boot display - // let display = Box::leak(Box::new(DisplayDeviceWrapper::new(display))); - // register_display_device(display); - - // let fbconsole = self.fbconsole.init(FramebufferConsole::from_framebuffer( - // (*display).deref(), - // None, - // )?); - - // debug::add_sink(fbconsole, LogLevel::Info); - - // // self.fbconsole.init(FramebufferConsole::from_framebuffer( - // // self.framebuffer.get(), - // // None, - // // )?); - // // debug::add_sink(self.fbconsole.get(), LogLevel::Info); - - // // // self.tty.init(CombinedTerminal::new(self.fbconsole.get())); - - // // devfs::add_named_block_device(self.framebuffer.get(), "fb0")?; - // // // devfs::add_char_device(self.tty.get(), CharDeviceType::TtyRegular)?; - // // console::add_console_autoflush(self.fbconsole.get()); - - // Ok(()) } fn init_initrd(initrd_start: PhysicalAddress, initrd_end: PhysicalAddress) { diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs index 580867db..27ceea72 100644 --- a/kernel/src/debug.rs +++ b/kernel/src/debug.rs @@ -8,7 +8,10 @@ use abi::error::Error; use libk::task::{process::Process, thread::Thread}; use libk_util::{ ring::RingBuffer, - sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, + sync::{ + spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard}, + IrqSafeSpinlock, + }, StaticVector, }; @@ -20,7 +23,7 @@ static RING_AVAILABLE: AtomicBool = AtomicBool::new(false); struct KernelLoggerSink; /// Locking log sink for dumping panic info pub struct PanicLoggerSink<'a> { - lock: IrqSafeSpinlockGuard<'a, StaticVector>, + lock: IrqSafeRwLockReadGuard<'a, StaticVector>, } struct RingLoggerInner { @@ -152,7 +155,7 @@ impl log::Log for KernelLoggerSink { if RING_AVAILABLE.load(Ordering::Acquire) { RING_LOGGER_SINK.log(record); } - for sink in DEBUG_SINKS.lock().iter_mut() { + for sink in DEBUG_SINKS.read().iter() { if sink.enabled(record.metadata()) { sink.log(record); } @@ -231,7 +234,7 @@ impl PanicLoggerSink<'_> { /// interrupted pub fn lock() -> Self { Self { - lock: DEBUG_SINKS.lock(), + lock: DEBUG_SINKS.read(), } } @@ -272,8 +275,8 @@ pub macro panic_log($sink:expr, $($args:tt)+) { static LOGGER: KernelLoggerSink = KernelLoggerSink; -static DEBUG_SINKS: IrqSafeSpinlock> = - IrqSafeSpinlock::new(StaticVector::new()); +static DEBUG_SINKS: IrqSafeRwLock> = + IrqSafeRwLock::new(StaticVector::new()); /// See [RingLoggerSink] pub static RING_LOGGER_SINK: RingLoggerSink = RingLoggerSink::new(); @@ -281,7 +284,7 @@ pub static RING_LOGGER_SINK: RingLoggerSink = RingLoggerSink::new(); /// Adds a debugging output sink pub fn add_sink(sink: &'static dyn DebugSink, level: LogLevel) { DEBUG_SINKS - .lock() + .write() .push(DebugSinkWrapper { inner: sink, level }); } diff --git a/kernel/src/device/display/console.rs b/kernel/src/device/display/console.rs index e8b40161..74c9426e 100644 --- a/kernel/src/device/display/console.rs +++ b/kernel/src/device/display/console.rs @@ -5,7 +5,7 @@ use core::time::Duration; use abi::{error::Error, io::TerminalSize, primitive_enum}; use alloc::{vec, vec::Vec}; use bitflags::bitflags; -use libk::{task::runtime, vfs::TerminalOutput}; +use libk::{device::display::Color, task::runtime, vfs::TerminalOutput}; use libk_util::{sync::IrqSafeSpinlock, StaticVector}; use crate::debug::DebugSink; @@ -14,7 +14,8 @@ const CONSOLE_ROW_LEN: usize = 80; const MAX_CSI_ARGS: usize = 8; const DEFAULT_FG_COLOR: ColorAttribute = ColorAttribute::White; -const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue; +/// Default console background color +pub const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue; primitive_enum! { #[allow(missing_docs)] @@ -49,19 +50,22 @@ impl ColorAttribute { } /// Converts the attribute to RGBA representation - pub fn as_rgba(&self, bold: bool) -> u32 { - let color = match self { - Self::Black => 0x000000, - Self::Red => 0x7F0000, - Self::Green => 0x007F00, - Self::Yellow => 0x7F7F00, - Self::Blue => 0x00007F, - Self::Magenta => 0x7F007F, - Self::Cyan => 0x007F7F, - Self::White => 0x7F7F7F, - }; + pub fn as_rgba(&self, bold: bool) -> Color { + const TABLE: &[Color] = &[ + Color::from_rgb_u32(0x000000), + Color::from_rgb_u32(0x7F0000), + Color::from_rgb_u32(0x007F00), + Color::from_rgb_u32(0x7F7F00), + Color::from_rgb_u32(0x00007F), + Color::from_rgb_u32(0x7F007F), + Color::from_rgb_u32(0x007F7F), + Color::from_rgb_u32(0x7F7F7F), + ]; + + let color = TABLE[*self as usize]; + if bold { - color * 2 + color.rgb_mul(2) } else { color } diff --git a/kernel/src/device/display/device.rs b/kernel/src/device/display/device.rs index 45e64bcb..2768f6e0 100644 --- a/kernel/src/device/display/device.rs +++ b/kernel/src/device/display/device.rs @@ -1,27 +1,15 @@ //! Display device structures and interfaces -use core::{ - mem::MaybeUninit, - ops::{Deref, DerefMut}, - sync::atomic::{AtomicU32, Ordering}, -}; +use core::ops::{Deref, DerefMut}; -use abi::{error::Error, io::DeviceRequest, process::ProcessId}; -use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec}; -use async_trait::async_trait; -use device_api::Device; -use libk::{ - device::display::{DisplayDevice, DisplayOwner}, - task::process::Process, - vfs::block::BlockDevice, -}; -use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider}; -use libk_util::sync::spin_rwlock::IrqSafeRwLock; +use abi::error::Error; +use libk::device::display::{Color, DisplayDevice, DisplayOwner, PixelFormat}; /// Convenience wrapper for accessing display framebuffers from the kernel side pub struct KernelFramebufferAccess<'a> { display: &'a dyn DisplayDevice, base: usize, stride: usize, + format: PixelFormat, size: usize, } @@ -31,30 +19,53 @@ impl<'a> KernelFramebufferAccess<'a> { // Lock the display by the kernel display.lock(DisplayOwner::Kernel)?; - match display.active_framebuffer() { - Ok(info) if let Some(kernel_base) = info.kernel_base => Ok(Self { - display, - base: kernel_base, - size: info.size / size_of::(), - stride: info.stride / size_of::(), - }), - _ => { - // Release the lock, couldn't get the framebuffer - display.unlock(DisplayOwner::Kernel).ok(); - Err(Error::InvalidOperation) - } - } + let Ok(framebuffer) = display.active_framebuffer() else { + display.unlock(DisplayOwner::Kernel).ok(); + return Err(Error::InvalidOperation); + }; + let Some(kernel_base) = framebuffer.kernel_base else { + display.unlock(DisplayOwner::Kernel).ok(); + return Err(Error::InvalidOperation); + }; + let Some(mode) = display.active_display_mode() else { + display.unlock(DisplayOwner::Kernel).ok(); + return Err(Error::InvalidOperation); + }; + + Ok(Self { + display, + base: kernel_base, + size: framebuffer.size / size_of::(), + stride: framebuffer.stride / size_of::(), + format: mode.pixel_format, + }) + } + + /// Returns the framebuffer pixel format + pub fn format(&self) -> PixelFormat { + self.format } /// Updates a pixel in the framebuffer #[inline] #[optimize(speed)] - pub fn set_pixel(&mut self, x: u32, y: u32, pixel: u32) { + pub fn set_pixel(&mut self, x: u32, y: u32, v: u32) { let p = y as usize * self.stride + x as usize; if p >= self.size { return; } - self[p] = pixel; + self[p] = v; + } + + /// Fills a rectangle with pixels + #[optimize(speed)] + pub fn fill_rect(&mut self, sx: u32, sy: u32, w: u32, h: u32, c: Color) { + let v = c.to_pixel_u32(self.format); + for y in sy..sy + h { + let ry = y as usize * self.stride..; + let row = &mut self[ry]; + row[sx as usize..sx as usize + w as usize].fill(v); + } } } diff --git a/kernel/src/device/display/fb_console.rs b/kernel/src/device/display/fb_console.rs index b818f355..efd3e271 100644 --- a/kernel/src/device/display/fb_console.rs +++ b/kernel/src/device/display/fb_console.rs @@ -1,18 +1,15 @@ //! Framebuffer console driver -use core::mem::MaybeUninit; - use abi::error::Error; -use libk::device::display::DisplayDevice; +use libk::device::display::{Color, DisplayDevice}; use libk_util::sync::IrqSafeSpinlock; use crate::debug::DebugSink; use super::{ - console::{Attributes, ConsoleBuffer, ConsoleState, DisplayConsole}, + console::{self, Attributes, ConsoleBuffer, ConsoleState, DisplayConsole}, device::KernelFramebufferAccess, font::PcScreenFont, - linear_fb::LinearFramebuffer, }; struct Inner { @@ -24,14 +21,15 @@ struct Inner { height: u32, cursor_row: u32, cursor_col: u32, + boot_framebuffer: bool, } struct DrawGlyph { sx: u32, sy: u32, c: u8, - fg: u32, - bg: u32, + fg: Color, + bg: Color, bytes_per_line: usize, } @@ -76,13 +74,13 @@ impl DisplayConsole for FramebufferConsole { let cursor_col = state.cursor_col; let cursor_row = state.cursor_row; - // inner.fill_rect( - // old_cursor_col * cw, - // old_cursor_row * ch, - // cw, - // ch, - // state.bg_color.as_rgba(false), - // ); + framebuffer.fill_rect( + old_cursor_col * cw, + old_cursor_row * ch, + cw, + ch, + state.bg_color.as_rgba(false), + ); while let Some((row_idx, row)) = iter.next_dirty() { if row_idx >= inner.height { @@ -112,14 +110,15 @@ impl DisplayConsole for FramebufferConsole { } } - // // Place cursor - // inner.fill_rect( - // cursor_col * cw, - // cursor_row * ch, - // cw, - // ch, - // state.fg_color.as_rgba(false), - // ); + // Place cursor + framebuffer.fill_rect( + cursor_col * cw, + cursor_row * ch, + cw, + ch, + state.fg_color.as_rgba(false), + ); + inner.display.synchronize().ok(); inner.cursor_col = cursor_col; inner.cursor_row = cursor_row; @@ -132,14 +131,11 @@ impl FramebufferConsole { // framebuffer: &'static LinearFramebuffer, display: &'static dyn DisplayDevice, font: Option>, + boot: bool, ) -> Result { - let mut framebuffer = [MaybeUninit::uninit()]; let mode = display .active_display_mode() .ok_or(Error::InvalidOperation)?; - display.active_framebuffers(&mut framebuffer).unwrap(); - - let framebuffer = unsafe { framebuffer[0].assume_init() }; let font = font.unwrap_or_default(); let char_width = font.width(); @@ -156,6 +152,7 @@ impl FramebufferConsole { char_height, cursor_row: 0, cursor_col: 0, + boot_framebuffer: boot, }; Ok(Self { @@ -163,6 +160,56 @@ impl FramebufferConsole { state: IrqSafeSpinlock::new(ConsoleState::new(buffer)), }) } + + /// Changes the display used for this console + pub fn set_display(&self, display: &'static dyn DisplayDevice) -> Result<(), Error> { + let mode = display + .active_display_mode() + .ok_or(Error::InvalidOperation)?; + + { + let mut old_inner = self.inner.lock(); + let mut old_state = self.state.lock(); + + let char_width = old_inner.char_width; + let char_height = old_inner.char_height; + + let buffer = ConsoleBuffer::new(mode.height / char_height)?; + + let inner = Inner { + display, + font: old_inner.font, + width: mode.width / char_width, + height: mode.height / char_height, + char_width, + char_height, + cursor_row: 0, + cursor_col: 0, + boot_framebuffer: false, + }; + + *old_inner = inner; + *old_state = ConsoleState::new(buffer); + } + + // Clear the display + if let Ok(mut framebuffer) = KernelFramebufferAccess::acquire(display) { + framebuffer.fill_rect( + 0, + 0, + mode.width, + mode.height, + console::DEFAULT_BG_COLOR.as_rgba(false), + ); + } + + Ok(()) + } + + /// Returns `true` if the console is using a boot-time framebuffer + pub fn uses_boot_framebuffer(&self) -> bool { + self.inner.lock().boot_framebuffer + } } fn draw_glyph( @@ -175,6 +222,10 @@ fn draw_glyph( c = b'?' as u32; } + let f = framebuffer.format(); + let fg = g.fg.to_pixel_u32(f); + let bg = g.bg.to_pixel_u32(f); + let mut glyph = font.raw_glyph_data(c); let mut y = 0; @@ -184,10 +235,8 @@ fn draw_glyph( let mut x = 0; while x < font.width() { - let v = if glyph[0] & mask != 0 { g.fg } else { g.bg }; - let v = v | 0xFF000000; + let v = if glyph[0] & mask != 0 { fg } else { bg }; framebuffer.set_pixel(g.sx + x, g.sy + y, v); - // framebuffer.set_pixel(g.sy + y, g.sx + x, v); mask >>= 1; x += 1; } @@ -196,57 +245,3 @@ fn draw_glyph( y += 1; } } - -// impl Inner { -// #[optimize(speed)] -// fn draw_glyph(&mut self, font: PcScreenFont<'static>, g: DrawGlyph) { -// let Ok(framebuffer) = KernelFramebufferAccess::acquire(self.display) else { -// return; -// }; -// -// // let Some(mut fb) = (unsafe { self.framebuffer.lock() }) else { -// // return; -// // }; -// -// // let mut c = g.c as u32; -// // if c >= font.len() { -// // c = b'?' as u32; -// // } -// -// // let mut glyph = font.raw_glyph_data(c); -// -// // let mut y = 0; -// -// // while y < font.height() { -// // let mut mask = 1 << (font.width() - 1); -// // let mut x = 0; -// -// // while x < font.width() { -// // let v = if glyph[0] & mask != 0 { g.fg } else { g.bg }; -// // let v = v | 0xFF000000; -// // fb[g.sy + y][(g.sx + x) as usize] = v; -// // mask >>= 1; -// // x += 1; -// // } -// -// // glyph = &glyph[g.bytes_per_line..]; -// // y += 1; -// // } -// } -// -// #[optimize(speed)] -// fn fill_rect(&mut self, x: u32, y: u32, w: u32, h: u32, val: u32) { -// if self.display.lock(DisplayOwner::Kernel).is_err() { -// return; -// } -// -// // let Some(mut fb) = (unsafe { self.framebuffer.lock() }) else { -// // return; -// // }; -// -// // for i in 0..h { -// // let row = &mut fb[i + y]; -// // row[x as usize..(x + w) as usize].fill(val); -// // } -// } -// } diff --git a/kernel/src/device/display/linear_fb.rs b/kernel/src/device/display/linear_fb.rs index 40b64597..a8a7532f 100644 --- a/kernel/src/device/display/linear_fb.rs +++ b/kernel/src/device/display/linear_fb.rs @@ -1,19 +1,11 @@ //! Abstract linear framebuffer device implementation -use core::{ - mem::MaybeUninit, - ops::{Index, IndexMut}, -}; +use core::mem::MaybeUninit; -use abi::{error::Error, io::DeviceRequest, process::ProcessId}; -use alloc::sync::Arc; +use abi::error::Error; use device_api::Device; -use libk::{ - device::display::{ - DisplayDevice, DisplayDimensions, DisplayMode, DisplayOwner, FramebufferInfo, PixelFormat, - }, - task::{process::Process, thread::Thread}, - vfs::block::BlockDevice, +use libk::device::display::{ + DisplayDevice, DisplayMode, DisplayOwner, DriverFlags, FramebufferInfo, PixelFormat, }; use libk_mm::{ address::PhysicalAddress, @@ -42,7 +34,6 @@ struct Inner { pub struct LinearFramebuffer { inner: IrqSafeSpinlock, phys_base: PhysicalAddress, - dimensions: DisplayDimensions, mode: DisplayMode, size: usize, } @@ -59,6 +50,7 @@ impl LinearFramebuffer { stride: usize, width: u32, height: u32, + pixel_format: PixelFormat, ) -> Result { let mapping = unsafe { RawDeviceMemoryMapping::map( @@ -80,12 +72,11 @@ impl LinearFramebuffer { width, height, frames_per_second: u32::MAX, - pixel_format: PixelFormat::R8G8B8A8, + pixel_format, }; let res = Self { inner: IrqSafeSpinlock::new(inner), - dimensions: DisplayDimensions { width, height }, phys_base, size, mode, @@ -134,6 +125,10 @@ impl Device for LinearFramebuffer { } impl DisplayDevice for LinearFramebuffer { + fn driver_flags(&self) -> DriverFlags { + DriverFlags::empty() + } + fn synchronize(&self) -> Result<(), Error> { Ok(()) } diff --git a/kernel/src/device/display/mod.rs b/kernel/src/device/display/mod.rs index f70d884e..c1d9d186 100644 --- a/kernel/src/device/display/mod.rs +++ b/kernel/src/device/display/mod.rs @@ -1,15 +1,5 @@ //! Display device interfaces -use core::{mem::MaybeUninit, ops::Deref}; - -use abi::error::Error; -use alloc::{boxed::Box, sync::Arc}; -use async_trait::async_trait; -use libk::{task::process::Process, vfs::block::BlockDevice}; -use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider}; - -use super::Device; - pub mod console; pub mod device; pub mod font; diff --git a/userspace/arch/aarch64/rc.d/99-colors b/userspace/arch/aarch64/rc.d/99-colors index fcbfee7f..28282e6d 100755 --- a/userspace/arch/aarch64/rc.d/99-colors +++ b/userspace/arch/aarch64/rc.d/99-colors @@ -1,3 +1,3 @@ #!/bin/sh -/sbin/service start /bin/colors +/sbin/service start /bin/colors /dev/fb0 diff --git a/userspace/arch/x86_64/rc.d/99-colors b/userspace/arch/x86_64/rc.d/99-colors index fcbfee7f..28282e6d 100755 --- a/userspace/arch/x86_64/rc.d/99-colors +++ b/userspace/arch/x86_64/rc.d/99-colors @@ -1,3 +1,3 @@ #!/bin/sh -/sbin/service start /bin/colors +/sbin/service start /bin/colors /dev/fb0 diff --git a/userspace/colors/src/display.rs b/userspace/colors/src/display.rs index 816cb4ac..94afc234 100644 --- a/userspace/colors/src/display.rs +++ b/userspace/colors/src/display.rs @@ -1,10 +1,11 @@ use std::{ - fs::{File, OpenOptions}, + fs::OpenOptions, mem::MaybeUninit, os::yggdrasil::io::{ device::{DeviceRequest, FdDeviceRequest}, mapping::FileMapping, }, + path::Path, }; use crate::error::Error; @@ -16,15 +17,16 @@ pub struct Display<'a> { width: usize, height: usize, - stride: usize, - size: usize, + // TODO use those + _stride: usize, + _size: usize, } pub struct Point(pub T, pub T); impl Display<'_> { - pub fn open() -> Result { - let file = OpenOptions::new().open("/dev/fb0")?; + pub fn open(framebuffer: impl AsRef) -> Result { + let file = OpenOptions::new().open(framebuffer)?; let framebuffer = unsafe { file.device_request(&mut DeviceRequest::AcquireDevice)?; @@ -51,8 +53,8 @@ impl Display<'_> { width, height, - stride: framebuffer.stride / size_of::(), - size: framebuffer.size / size_of::(), + _stride: framebuffer.stride / size_of::(), + _size: framebuffer.size / size_of::(), }) } diff --git a/userspace/colors/src/main.rs b/userspace/colors/src/main.rs index 72b1fc9b..0b63f1fe 100644 --- a/userspace/colors/src/main.rs +++ b/userspace/colors/src/main.rs @@ -3,15 +3,13 @@ // TODO rewrite and split this into meaningful components use std::{ - collections::{BTreeMap, HashMap}, - os::{ + collections::{BTreeMap, HashMap}, env, os::{ fd::{AsRawFd, RawFd}, yggdrasil::io::{ mapping::FileMapping, poll::PollChannel, shared_memory::SharedMemory, }, - }, - process::{Command, ExitCode}, + }, process::{Command, ExitCode} }; use display::Display; @@ -142,10 +140,10 @@ impl Row { } impl<'a> Server<'a, '_> { - pub fn new() -> Result { + pub fn new(framebuffer: &str) -> Result { let mut poll = PollChannel::new()?; - let mut display = Display::open()?; + let mut display = Display::open(framebuffer)?; let input = KeyboardInput::open()?; let (sender, receiver) = serde_ipc::listen(libcolors::CHANNEL_NAME)?; @@ -156,6 +154,7 @@ impl<'a> Server<'a, '_> { let background = 0xFFCCCCCC; display.fill(background); + display.flush(); Ok(Self { display, @@ -587,7 +586,10 @@ impl ServerSender { } fn main() -> ExitCode { - let server = Server::new().unwrap(); + let args = env::args().skip(1).collect::>(); + let framebuffer = args.get(0).map_or("/dev/fb0", |s| s.as_str()); + + let server = Server::new(framebuffer).unwrap(); server.run() }