video: x86_64 switch-over from boot fb to virtio-gpu if present

This commit is contained in:
Mark Poliakov 2024-12-08 14:49:29 +02:00
parent 6bd3d387bf
commit 8c09e046e9
20 changed files with 400 additions and 248 deletions

View File

@ -4,6 +4,9 @@ use bytemuck::{Pod, Zeroable};
use crate::{AvailableRegion, IterableMemoryMap, LoadProtocolHeader}; use crate::{AvailableRegion, IterableMemoryMap, LoadProtocolHeader};
pub const PIXEL_R8G8B8A8: u32 = 1;
pub const PIXEL_B8G8R8A8: u32 = 2;
#[derive(Clone, Copy, Pod, Zeroable)] #[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)] #[repr(C)]
pub struct LoadProtocol { pub struct LoadProtocol {
@ -46,6 +49,8 @@ pub struct FramebufferOption {
pub res_stride: u64, pub res_stride: u64,
pub res_address: u64, pub res_address: u64,
pub res_size: u64, pub res_size: u64,
pub res_format: u32,
pub _0: u32,
} }
impl AvailableRegion for AvailableMemoryRegion { impl AvailableRegion for AvailableMemoryRegion {

View File

@ -14,7 +14,9 @@ use log::{debug, error, info};
use uefi::{ use uefi::{
prelude::*, prelude::*,
proto::{ proto::{
console::gop::GraphicsOutput, device_path::DevicePath, loaded_image::LoadedImage, console::gop::{GraphicsOutput, PixelFormat},
device_path::DevicePath,
loaded_image::LoadedImage,
media::fs::SimpleFileSystem, media::fs::SimpleFileSystem,
}, },
table::{ table::{
@ -24,7 +26,7 @@ use uefi::{
Error, Error,
}; };
use yboot_proto::{ use yboot_proto::{
v1::{AvailableMemoryRegion, FramebufferOption}, v1::{self, AvailableMemoryRegion, FramebufferOption},
LoadProtocolV1, LOADER_MAGIC, LoadProtocolV1, LOADER_MAGIC,
}; };
@ -47,11 +49,18 @@ fn setup_framebuffer(bs: &BootServices, fb: &mut FramebufferOption) -> Result<()
let mut result = gop.frame_buffer(); 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_width = fb.req_width;
fb.res_height = fb.req_height; fb.res_height = fb.req_height;
fb.res_address = result.as_mut_ptr() as _; fb.res_address = result.as_mut_ptr() as _;
fb.res_stride = mode.info().stride() as u64 * 4; fb.res_stride = mode.info().stride() as u64 * 4;
fb.res_size = result.size() as _; fb.res_size = result.size() as _;
fb.res_format = format;
info!( info!(
"Framebuffer: {}x{} @ {:#x}", "Framebuffer: {}x{} @ {:#x}",

View File

@ -1,9 +1,6 @@
use core::{future::poll_fn, mem::size_of, ptr::null_mut, task::Poll}; use core::{future::poll_fn, mem::size_of, ptr::null_mut, task::Poll};
use alloc::{ use alloc::collections::{BTreeMap, BTreeSet};
collections::{BTreeMap, BTreeSet},
vec::Vec,
};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use libk_mm::{ use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress}, address::{AsPhysicalAddress, PhysicalAddress},
@ -96,6 +93,7 @@ pub struct QueuePair {
pub struct PrpList { pub struct PrpList {
prp1: PhysicalRegionPage, prp1: PhysicalRegionPage,
prp2: PhysicalRegionPage, prp2: PhysicalRegionPage,
#[allow(unused)]
list: Option<PageBox<[PhysicalAddress]>>, list: Option<PageBox<[PhysicalAddress]>>,
} }

View File

@ -2,7 +2,11 @@
use core::sync::atomic::{AtomicUsize, Ordering}; use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::{format, string::String}; 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 libk_util::OneTimeInit;
use yggdrasil_abi::error::Error; use yggdrasil_abi::error::Error;
@ -39,6 +43,16 @@ pub fn root() -> &'static NodeRef {
DEVFS_ROOT.get() 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 /// Adds a character device with a custom name
pub fn add_named_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Error> { pub fn add_named_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Error> {
log::info!("Add char device: {}", name); 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<S: Into<String>>( pub fn add_named_block_device<S: Into<String>>(
dev: &'static dyn BlockDevice, dev: &'static dyn BlockDevice,
name: S, name: S,
) -> Result<(), Error> { ) -> Result<NodeRef, Error> {
let name = name.into(); let name = name.into();
log::info!("Add block device: {}", name); log::info!("Add block device: {}", name);
let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS); 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<NodeRef, Error> {
static FB_COUNT: AtomicUsize = AtomicUsize::new(0); static FB_COUNT: AtomicUsize = AtomicUsize::new(0);
let (count, prefix) = match kind { let (count, prefix) = match kind {

View File

@ -1,4 +1,3 @@
use alloc::vec::Vec;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use libk::{device::display::PixelFormat, error::Error}; use libk::{device::display::PixelFormat, error::Error};
use libk_mm::{address::PhysicalAddress, PageBox}; use libk_mm::{address::PhysicalAddress, PageBox};

View File

@ -5,16 +5,14 @@ extern crate alloc;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use bytemuck::{Pod, Zeroable}; use command::{ControlLock, ScanoutInfo};
use command::{ControlLock, Rect, ScanoutInfo};
use device_api::Device; use device_api::Device;
use libk::device::display::{ use libk::device::display::{
register_display_device, DisplayDevice, DisplayDeviceWrapper, DisplayMode, DisplayOwner, register_display_device, DisplayDevice, DisplayDeviceWrapper, DisplayMode, DisplayOwner,
FramebufferInfo, PixelFormat, DriverFlags, FramebufferInfo, PixelFormat,
}; };
use libk_mm::{ use libk_mm::{
address::{PhysicalAddress, Virtualize}, address::{PhysicalAddress, Virtualize},
device::RawDeviceMemoryMapping,
phys, phys,
table::MapAttributes, table::MapAttributes,
PageBox, PageProvider, L3_PAGE_SIZE, PageBox, PageProvider, L3_PAGE_SIZE,
@ -59,6 +57,7 @@ struct Config {
pub struct VirtioGpu<T: Transport> { pub struct VirtioGpu<T: Transport> {
transport: IrqSafeSpinlock<T>, transport: IrqSafeSpinlock<T>,
#[allow(unused)]
pci_device_info: Option<PciDeviceInfo>, pci_device_info: Option<PciDeviceInfo>,
queues: OneTimeInit<Queues>, queues: OneTimeInit<Queues>,
@ -296,19 +295,23 @@ impl<T: Transport + 'static> PageProvider for VirtioGpu<T> {
fn clone_page( fn clone_page(
&self, &self,
offset: u64, _offset: u64,
src_phys: PhysicalAddress, _src_phys: PhysicalAddress,
src_attrs: MapAttributes, _src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> { ) -> Result<PhysicalAddress, Error> {
todo!() todo!()
} }
fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> { fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
Ok(()) Ok(())
} }
} }
impl<T: Transport + 'static> DisplayDevice for VirtioGpu<T> { impl<T: Transport + 'static> DisplayDevice for VirtioGpu<T> {
fn driver_flags(&self) -> DriverFlags {
DriverFlags::REPLACES_BOOT_FRAMEBUFFER
}
fn lock(&self, owner: DisplayOwner) -> Result<(), Error> { fn lock(&self, owner: DisplayOwner) -> Result<(), Error> {
let mut config = self.config.write(); let mut config = self.config.write();
if config.owner == owner { if config.owner == owner {
@ -341,7 +344,7 @@ impl<T: Transport + 'static> DisplayDevice for VirtioGpu<T> {
self.flush() 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!() todo!()
} }
@ -383,7 +386,7 @@ impl<T: Transport + 'static> DisplayDevice for VirtioGpu<T> {
}) })
} }
fn query_display_modes(&self, modes: &mut [MaybeUninit<DisplayMode>]) -> Result<usize, Error> { fn query_display_modes(&self, _modes: &mut [MaybeUninit<DisplayMode>]) -> Result<usize, Error> {
todo!() todo!()
} }
} }

View File

@ -10,6 +10,7 @@ use device_api::Device;
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider}; use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit}; use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use yggdrasil_abi::{ use yggdrasil_abi::{
bitflags,
error::Error, error::Error,
io::{DeviceRequest, Framebuffer}, io::{DeviceRequest, Framebuffer},
process::ProcessId, process::ProcessId,
@ -17,6 +18,22 @@ use yggdrasil_abi::{
use crate::{task::thread::Thread, vfs::block::BlockDevice}; 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 /// Represents the current lock holder for a display device
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisplayOwner { pub enum DisplayOwner {
@ -81,6 +98,9 @@ pub struct FramebufferInfo {
/// Abstract display device interface /// Abstract display device interface
pub trait DisplayDevice: Device + PageProvider { pub trait DisplayDevice: Device + PageProvider {
/// Returns the init-time driver flags
fn driver_flags(&self) -> DriverFlags;
/// Returns the active display mode /// Returns the active display mode
fn active_display_mode(&self) -> Option<DisplayMode>; fn active_display_mode(&self) -> Option<DisplayMode>;
/// Returns the list of available display modes /// Returns the list of available display modes
@ -221,3 +241,81 @@ impl Deref for DisplayDeviceWrapper {
self.inner 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);
// }
}

View File

@ -282,7 +282,7 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>( pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self, &self,
block_position: u64, block_position: u64,
size: usize, _size: usize,
mapper: F, mapper: F,
) -> Result<T, Error> { ) -> Result<T, Error> {
let segment_position = block_position & !(self.segment_size as u64 - 1); let segment_position = block_position & !(self.segment_size as u64 - 1);

View File

@ -55,6 +55,8 @@ static YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 {
res_stride: 0, res_stride: 0,
res_address: 0, res_address: 0,
res_size: 0, res_size: 0,
res_format: 0,
_0: 0,
}, },
}; };

View File

@ -3,7 +3,7 @@ use core::{
mem::size_of, mem::size_of,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
ptr::null_mut, ptr::null_mut,
sync::atomic::{AtomicU32, Ordering}, sync::atomic::Ordering,
}; };
use ::acpi::{mcfg::Mcfg, AcpiTables, InterruptModel}; use ::acpi::{mcfg::Mcfg, AcpiTables, InterruptModel};
@ -29,6 +29,7 @@ use libk::{
device::{ device::{
display::{ display::{
register_display_device, set_display_registration_callback, DisplayDeviceWrapper, register_display_device, set_display_registration_callback, DisplayDeviceWrapper,
DriverFlags, PixelFormat,
}, },
register_external_interrupt_controller, register_external_interrupt_controller,
}, },
@ -40,7 +41,10 @@ use libk_mm::{
table::{EntryLevel, EntryLevelExt}, table::{EntryLevel, EntryLevelExt},
}; };
use libk_util::{sync::SpinFence, OneTimeInit}; use libk_util::{sync::SpinFence, OneTimeInit};
use yboot_proto::{v1::AvailableMemoryRegion, LoadProtocolV1}; use yboot_proto::{
v1::{self, AvailableMemoryRegion},
LoadProtocolV1,
};
use ygg_driver_pci::PciBusManager; use ygg_driver_pci::PciBusManager;
mod acpi; mod acpi;
@ -55,7 +59,7 @@ use crate::{
debug::{self, LogLevel}, debug::{self, LogLevel},
device::{ device::{
self, self,
display::{console, fb_console::FramebufferConsole, linear_fb::LinearFramebuffer}, display::{fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
}, },
fs::{Initrd, INITRD_DATA}, fs::{Initrd, INITRD_DATA},
}; };
@ -348,7 +352,26 @@ impl X86_64 {
set_display_registration_callback(|device| { set_display_registration_callback(|device| {
log::info!("Display registered: {:?}", device.display_name()); 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()?; self.init_framebuffer()?;
@ -425,53 +448,47 @@ impl X86_64 {
} }
unsafe fn init_framebuffer(&'static self) -> Result<(), Error> { 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(()) 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) { fn init_initrd(initrd_start: PhysicalAddress, initrd_end: PhysicalAddress) {

View File

@ -8,7 +8,10 @@ use abi::error::Error;
use libk::task::{process::Process, thread::Thread}; use libk::task::{process::Process, thread::Thread};
use libk_util::{ use libk_util::{
ring::RingBuffer, ring::RingBuffer,
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, sync::{
spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard},
IrqSafeSpinlock,
},
StaticVector, StaticVector,
}; };
@ -20,7 +23,7 @@ static RING_AVAILABLE: AtomicBool = AtomicBool::new(false);
struct KernelLoggerSink; struct KernelLoggerSink;
/// Locking log sink for dumping panic info /// Locking log sink for dumping panic info
pub struct PanicLoggerSink<'a> { pub struct PanicLoggerSink<'a> {
lock: IrqSafeSpinlockGuard<'a, StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS>>, lock: IrqSafeRwLockReadGuard<'a, StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS>>,
} }
struct RingLoggerInner { struct RingLoggerInner {
@ -152,7 +155,7 @@ impl log::Log for KernelLoggerSink {
if RING_AVAILABLE.load(Ordering::Acquire) { if RING_AVAILABLE.load(Ordering::Acquire) {
RING_LOGGER_SINK.log(record); 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()) { if sink.enabled(record.metadata()) {
sink.log(record); sink.log(record);
} }
@ -231,7 +234,7 @@ impl PanicLoggerSink<'_> {
/// interrupted /// interrupted
pub fn lock() -> Self { pub fn lock() -> Self {
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 LOGGER: KernelLoggerSink = KernelLoggerSink;
static DEBUG_SINKS: IrqSafeSpinlock<StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS>> = static DEBUG_SINKS: IrqSafeRwLock<StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS>> =
IrqSafeSpinlock::new(StaticVector::new()); IrqSafeRwLock::new(StaticVector::new());
/// See [RingLoggerSink] /// See [RingLoggerSink]
pub static RING_LOGGER_SINK: RingLoggerSink = RingLoggerSink::new(); 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 /// Adds a debugging output sink
pub fn add_sink(sink: &'static dyn DebugSink, level: LogLevel) { pub fn add_sink(sink: &'static dyn DebugSink, level: LogLevel) {
DEBUG_SINKS DEBUG_SINKS
.lock() .write()
.push(DebugSinkWrapper { inner: sink, level }); .push(DebugSinkWrapper { inner: sink, level });
} }

View File

@ -5,7 +5,7 @@ use core::time::Duration;
use abi::{error::Error, io::TerminalSize, primitive_enum}; use abi::{error::Error, io::TerminalSize, primitive_enum};
use alloc::{vec, vec::Vec}; use alloc::{vec, vec::Vec};
use bitflags::bitflags; 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 libk_util::{sync::IrqSafeSpinlock, StaticVector};
use crate::debug::DebugSink; use crate::debug::DebugSink;
@ -14,7 +14,8 @@ const CONSOLE_ROW_LEN: usize = 80;
const MAX_CSI_ARGS: usize = 8; const MAX_CSI_ARGS: usize = 8;
const DEFAULT_FG_COLOR: ColorAttribute = ColorAttribute::White; 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! { primitive_enum! {
#[allow(missing_docs)] #[allow(missing_docs)]
@ -49,19 +50,22 @@ impl ColorAttribute {
} }
/// Converts the attribute to RGBA representation /// Converts the attribute to RGBA representation
pub fn as_rgba(&self, bold: bool) -> u32 { pub fn as_rgba(&self, bold: bool) -> Color {
let color = match self { const TABLE: &[Color] = &[
Self::Black => 0x000000, Color::from_rgb_u32(0x000000),
Self::Red => 0x7F0000, Color::from_rgb_u32(0x7F0000),
Self::Green => 0x007F00, Color::from_rgb_u32(0x007F00),
Self::Yellow => 0x7F7F00, Color::from_rgb_u32(0x7F7F00),
Self::Blue => 0x00007F, Color::from_rgb_u32(0x00007F),
Self::Magenta => 0x7F007F, Color::from_rgb_u32(0x7F007F),
Self::Cyan => 0x007F7F, Color::from_rgb_u32(0x007F7F),
Self::White => 0x7F7F7F, Color::from_rgb_u32(0x7F7F7F),
}; ];
let color = TABLE[*self as usize];
if bold { if bold {
color * 2 color.rgb_mul(2)
} else { } else {
color color
} }

View File

@ -1,27 +1,15 @@
//! Display device structures and interfaces //! Display device structures and interfaces
use core::{ use core::ops::{Deref, DerefMut};
mem::MaybeUninit,
ops::{Deref, DerefMut},
sync::atomic::{AtomicU32, Ordering},
};
use abi::{error::Error, io::DeviceRequest, process::ProcessId}; use abi::error::Error;
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec}; use libk::device::display::{Color, DisplayDevice, DisplayOwner, PixelFormat};
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;
/// Convenience wrapper for accessing display framebuffers from the kernel side /// Convenience wrapper for accessing display framebuffers from the kernel side
pub struct KernelFramebufferAccess<'a> { pub struct KernelFramebufferAccess<'a> {
display: &'a dyn DisplayDevice, display: &'a dyn DisplayDevice,
base: usize, base: usize,
stride: usize, stride: usize,
format: PixelFormat,
size: usize, size: usize,
} }
@ -31,30 +19,53 @@ impl<'a> KernelFramebufferAccess<'a> {
// Lock the display by the kernel // Lock the display by the kernel
display.lock(DisplayOwner::Kernel)?; display.lock(DisplayOwner::Kernel)?;
match display.active_framebuffer() { let Ok(framebuffer) = display.active_framebuffer() else {
Ok(info) if let Some(kernel_base) = info.kernel_base => Ok(Self { display.unlock(DisplayOwner::Kernel).ok();
display, return Err(Error::InvalidOperation);
base: kernel_base, };
size: info.size / size_of::<u32>(), let Some(kernel_base) = framebuffer.kernel_base else {
stride: info.stride / size_of::<u32>(), display.unlock(DisplayOwner::Kernel).ok();
}), return Err(Error::InvalidOperation);
_ => { };
// Release the lock, couldn't get the framebuffer let Some(mode) = display.active_display_mode() else {
display.unlock(DisplayOwner::Kernel).ok(); display.unlock(DisplayOwner::Kernel).ok();
Err(Error::InvalidOperation) return Err(Error::InvalidOperation);
} };
}
Ok(Self {
display,
base: kernel_base,
size: framebuffer.size / size_of::<u32>(),
stride: framebuffer.stride / size_of::<u32>(),
format: mode.pixel_format,
})
}
/// Returns the framebuffer pixel format
pub fn format(&self) -> PixelFormat {
self.format
} }
/// Updates a pixel in the framebuffer /// Updates a pixel in the framebuffer
#[inline] #[inline]
#[optimize(speed)] #[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; let p = y as usize * self.stride + x as usize;
if p >= self.size { if p >= self.size {
return; 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);
}
} }
} }

View File

@ -1,18 +1,15 @@
//! Framebuffer console driver //! Framebuffer console driver
use core::mem::MaybeUninit;
use abi::error::Error; use abi::error::Error;
use libk::device::display::DisplayDevice; use libk::device::display::{Color, DisplayDevice};
use libk_util::sync::IrqSafeSpinlock; use libk_util::sync::IrqSafeSpinlock;
use crate::debug::DebugSink; use crate::debug::DebugSink;
use super::{ use super::{
console::{Attributes, ConsoleBuffer, ConsoleState, DisplayConsole}, console::{self, Attributes, ConsoleBuffer, ConsoleState, DisplayConsole},
device::KernelFramebufferAccess, device::KernelFramebufferAccess,
font::PcScreenFont, font::PcScreenFont,
linear_fb::LinearFramebuffer,
}; };
struct Inner { struct Inner {
@ -24,14 +21,15 @@ struct Inner {
height: u32, height: u32,
cursor_row: u32, cursor_row: u32,
cursor_col: u32, cursor_col: u32,
boot_framebuffer: bool,
} }
struct DrawGlyph { struct DrawGlyph {
sx: u32, sx: u32,
sy: u32, sy: u32,
c: u8, c: u8,
fg: u32, fg: Color,
bg: u32, bg: Color,
bytes_per_line: usize, bytes_per_line: usize,
} }
@ -76,13 +74,13 @@ impl DisplayConsole for FramebufferConsole {
let cursor_col = state.cursor_col; let cursor_col = state.cursor_col;
let cursor_row = state.cursor_row; let cursor_row = state.cursor_row;
// inner.fill_rect( framebuffer.fill_rect(
// old_cursor_col * cw, old_cursor_col * cw,
// old_cursor_row * ch, old_cursor_row * ch,
// cw, cw,
// ch, ch,
// state.bg_color.as_rgba(false), state.bg_color.as_rgba(false),
// ); );
while let Some((row_idx, row)) = iter.next_dirty() { while let Some((row_idx, row)) = iter.next_dirty() {
if row_idx >= inner.height { if row_idx >= inner.height {
@ -112,14 +110,15 @@ impl DisplayConsole for FramebufferConsole {
} }
} }
// // Place cursor // Place cursor
// inner.fill_rect( framebuffer.fill_rect(
// cursor_col * cw, cursor_col * cw,
// cursor_row * ch, cursor_row * ch,
// cw, cw,
// ch, ch,
// state.fg_color.as_rgba(false), state.fg_color.as_rgba(false),
// ); );
inner.display.synchronize().ok();
inner.cursor_col = cursor_col; inner.cursor_col = cursor_col;
inner.cursor_row = cursor_row; inner.cursor_row = cursor_row;
@ -132,14 +131,11 @@ impl FramebufferConsole {
// framebuffer: &'static LinearFramebuffer, // framebuffer: &'static LinearFramebuffer,
display: &'static dyn DisplayDevice, display: &'static dyn DisplayDevice,
font: Option<PcScreenFont<'static>>, font: Option<PcScreenFont<'static>>,
boot: bool,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut framebuffer = [MaybeUninit::uninit()];
let mode = display let mode = display
.active_display_mode() .active_display_mode()
.ok_or(Error::InvalidOperation)?; .ok_or(Error::InvalidOperation)?;
display.active_framebuffers(&mut framebuffer).unwrap();
let framebuffer = unsafe { framebuffer[0].assume_init() };
let font = font.unwrap_or_default(); let font = font.unwrap_or_default();
let char_width = font.width(); let char_width = font.width();
@ -156,6 +152,7 @@ impl FramebufferConsole {
char_height, char_height,
cursor_row: 0, cursor_row: 0,
cursor_col: 0, cursor_col: 0,
boot_framebuffer: boot,
}; };
Ok(Self { Ok(Self {
@ -163,6 +160,56 @@ impl FramebufferConsole {
state: IrqSafeSpinlock::new(ConsoleState::new(buffer)), 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( fn draw_glyph(
@ -175,6 +222,10 @@ fn draw_glyph(
c = b'?' as u32; 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 glyph = font.raw_glyph_data(c);
let mut y = 0; let mut y = 0;
@ -184,10 +235,8 @@ fn draw_glyph(
let mut x = 0; let mut x = 0;
while x < font.width() { while x < font.width() {
let v = if glyph[0] & mask != 0 { g.fg } else { g.bg }; let v = if glyph[0] & mask != 0 { fg } else { bg };
let v = v | 0xFF000000;
framebuffer.set_pixel(g.sx + x, g.sy + y, v); framebuffer.set_pixel(g.sx + x, g.sy + y, v);
// framebuffer.set_pixel(g.sy + y, g.sx + x, v);
mask >>= 1; mask >>= 1;
x += 1; x += 1;
} }
@ -196,57 +245,3 @@ fn draw_glyph(
y += 1; 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);
// // }
// }
// }

View File

@ -1,19 +1,11 @@
//! Abstract linear framebuffer device implementation //! Abstract linear framebuffer device implementation
use core::{ use core::mem::MaybeUninit;
mem::MaybeUninit,
ops::{Index, IndexMut},
};
use abi::{error::Error, io::DeviceRequest, process::ProcessId}; use abi::error::Error;
use alloc::sync::Arc;
use device_api::Device; use device_api::Device;
use libk::{ use libk::device::display::{
device::display::{ DisplayDevice, DisplayMode, DisplayOwner, DriverFlags, FramebufferInfo, PixelFormat,
DisplayDevice, DisplayDimensions, DisplayMode, DisplayOwner, FramebufferInfo, PixelFormat,
},
task::{process::Process, thread::Thread},
vfs::block::BlockDevice,
}; };
use libk_mm::{ use libk_mm::{
address::PhysicalAddress, address::PhysicalAddress,
@ -42,7 +34,6 @@ struct Inner {
pub struct LinearFramebuffer { pub struct LinearFramebuffer {
inner: IrqSafeSpinlock<Inner>, inner: IrqSafeSpinlock<Inner>,
phys_base: PhysicalAddress, phys_base: PhysicalAddress,
dimensions: DisplayDimensions,
mode: DisplayMode, mode: DisplayMode,
size: usize, size: usize,
} }
@ -59,6 +50,7 @@ impl LinearFramebuffer {
stride: usize, stride: usize,
width: u32, width: u32,
height: u32, height: u32,
pixel_format: PixelFormat,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mapping = unsafe { let mapping = unsafe {
RawDeviceMemoryMapping::map( RawDeviceMemoryMapping::map(
@ -80,12 +72,11 @@ impl LinearFramebuffer {
width, width,
height, height,
frames_per_second: u32::MAX, frames_per_second: u32::MAX,
pixel_format: PixelFormat::R8G8B8A8, pixel_format,
}; };
let res = Self { let res = Self {
inner: IrqSafeSpinlock::new(inner), inner: IrqSafeSpinlock::new(inner),
dimensions: DisplayDimensions { width, height },
phys_base, phys_base,
size, size,
mode, mode,
@ -134,6 +125,10 @@ impl Device for LinearFramebuffer {
} }
impl DisplayDevice for LinearFramebuffer { impl DisplayDevice for LinearFramebuffer {
fn driver_flags(&self) -> DriverFlags {
DriverFlags::empty()
}
fn synchronize(&self) -> Result<(), Error> { fn synchronize(&self) -> Result<(), Error> {
Ok(()) Ok(())
} }

View File

@ -1,15 +1,5 @@
//! Display device interfaces //! 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 console;
pub mod device; pub mod device;
pub mod font; pub mod font;

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
/sbin/service start /bin/colors /sbin/service start /bin/colors /dev/fb0

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
/sbin/service start /bin/colors /sbin/service start /bin/colors /dev/fb0

View File

@ -1,10 +1,11 @@
use std::{ use std::{
fs::{File, OpenOptions}, fs::OpenOptions,
mem::MaybeUninit, mem::MaybeUninit,
os::yggdrasil::io::{ os::yggdrasil::io::{
device::{DeviceRequest, FdDeviceRequest}, device::{DeviceRequest, FdDeviceRequest},
mapping::FileMapping, mapping::FileMapping,
}, },
path::Path,
}; };
use crate::error::Error; use crate::error::Error;
@ -16,15 +17,16 @@ pub struct Display<'a> {
width: usize, width: usize,
height: usize, height: usize,
stride: usize, // TODO use those
size: usize, _stride: usize,
_size: usize,
} }
pub struct Point<T>(pub T, pub T); pub struct Point<T>(pub T, pub T);
impl Display<'_> { impl Display<'_> {
pub fn open() -> Result<Self, Error> { pub fn open(framebuffer: impl AsRef<Path>) -> Result<Self, Error> {
let file = OpenOptions::new().open("/dev/fb0")?; let file = OpenOptions::new().open(framebuffer)?;
let framebuffer = unsafe { let framebuffer = unsafe {
file.device_request(&mut DeviceRequest::AcquireDevice)?; file.device_request(&mut DeviceRequest::AcquireDevice)?;
@ -51,8 +53,8 @@ impl Display<'_> {
width, width,
height, height,
stride: framebuffer.stride / size_of::<u32>(), _stride: framebuffer.stride / size_of::<u32>(),
size: framebuffer.size / size_of::<u32>(), _size: framebuffer.size / size_of::<u32>(),
}) })
} }

View File

@ -3,15 +3,13 @@
// TODO rewrite and split this into meaningful components // TODO rewrite and split this into meaningful components
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap}, env, os::{
os::{
fd::{AsRawFd, RawFd}, fd::{AsRawFd, RawFd},
yggdrasil::io::{ yggdrasil::io::{
mapping::FileMapping, poll::PollChannel, mapping::FileMapping, poll::PollChannel,
shared_memory::SharedMemory, shared_memory::SharedMemory,
}, },
}, }, process::{Command, ExitCode}
process::{Command, ExitCode},
}; };
use display::Display; use display::Display;
@ -142,10 +140,10 @@ impl Row {
} }
impl<'a> Server<'a, '_> { impl<'a> Server<'a, '_> {
pub fn new() -> Result<Self, Error> { pub fn new(framebuffer: &str) -> Result<Self, Error> {
let mut poll = PollChannel::new()?; let mut poll = PollChannel::new()?;
let mut display = Display::open()?; let mut display = Display::open(framebuffer)?;
let input = KeyboardInput::open()?; let input = KeyboardInput::open()?;
let (sender, receiver) = serde_ipc::listen(libcolors::CHANNEL_NAME)?; let (sender, receiver) = serde_ipc::listen(libcolors::CHANNEL_NAME)?;
@ -156,6 +154,7 @@ impl<'a> Server<'a, '_> {
let background = 0xFFCCCCCC; let background = 0xFFCCCCCC;
display.fill(background); display.fill(background);
display.flush();
Ok(Self { Ok(Self {
display, display,
@ -587,7 +586,10 @@ impl ServerSender {
} }
fn main() -> ExitCode { fn main() -> ExitCode {
let server = Server::new().unwrap(); let args = env::args().skip(1).collect::<Vec<_>>();
let framebuffer = args.get(0).map_or("/dev/fb0", |s| s.as_str());
let server = Server::new(framebuffer).unwrap();
server.run() server.run()
} }