diff --git a/kernel/libk/src/panic.rs b/kernel/libk/src/panic.rs index 94830dd1..0f8777fb 100644 --- a/kernel/libk/src/panic.rs +++ b/kernel/libk/src/panic.rs @@ -14,7 +14,10 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! { if let Some(ph) = PANIC_HANDLER.try_get() { ph(pi); } else { - log::error!("{:#?}", pi); + log::error!("Kernel panic: {}", pi.message()); + if let Some(location) = pi.location() { + log::error!(" at {location}"); + } loop { unsafe { diff --git a/kernel/src/device/mbox/bcm2835_mbox.rs b/kernel/src/device/mbox/bcm2835_mbox.rs new file mode 100644 index 00000000..063f9dad --- /dev/null +++ b/kernel/src/device/mbox/bcm2835_mbox.rs @@ -0,0 +1,341 @@ +#![allow(missing_docs)] + +use core::{cell::UnsafeCell, mem::MaybeUninit}; + +use abi::error::Error; +use alloc::sync::Arc; +use device_api::device::{Device, DeviceInitContext}; +use device_tree::driver::{device_tree_driver, Node, ProbeContext}; +use kernel_arch_aarch64::mem::table::L3; +use libk::device::{ + display::{ + DisplayDevice, DisplayMode, DisplayOwner, DriverFlags, FramebufferInfo, PixelFormat, + }, + manager::DEVICE_REGISTRY, +}; +use libk_mm::{ + address::{AsPhysicalAddress, PhysicalAddress}, + device::DeviceMemoryIo, + table::{EntryLevel, MapAttributes}, + OnDemandPage, PageBox, PageProvider, VirtualPage, +}; +use libk_util::sync::IrqSafeSpinlock; +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_bitfields, register_structs, + registers::{ReadOnly, ReadWrite, WriteOnly}, +}; + +register_bitfields! { + u32, + MboxValue [ + ADDRESS OFFSET(4) NUMBITS(28) [], + CHANNEL OFFSET(0) NUMBITS(4) [ + PowerManagement = 0, + Framebuffer = 1, + VirtualUart = 2, + Vchiq = 3, + Led = 4, + Button = 5, + Touch = 6, + PropertyArmToVc = 8, + PropertyVcToArm = 9 + ] + ], + MboxStatus [ + FULL OFFSET(31) NUMBITS(1) [], + EMPTY OFFSET(30) NUMBITS(1) [], + ] +} + +register_structs! { + #[allow(non_snake_case)] + Regs { + (0x00 => READ: ReadOnly), + (0x04 => _0), + (0x10 => POLL: ReadOnly), + (0x14 => SENDER: ReadOnly), + (0x18 => STATUS: ReadOnly), + (0x1C => CONFIG: ReadWrite), + (0x20 => WRITE: WriteOnly), + (0x24 => _1), + (0x40 => @END), + } +} + +struct Bcm2835Mbox { + regs: IrqSafeSpinlock>, +} + +struct Bcm2835Framebuffer { + mode: DisplayMode, + base: PhysicalAddress, + size: usize, + pitch: usize, +} + +struct FramebufferResponse { + physical_address: u32, + size: u32, + pitch: u32, +} + +unsafe impl Send for Bcm2835Mbox {} +unsafe impl Sync for Bcm2835Mbox {} + +impl Device for Bcm2835Mbox { + fn display_name(&self) -> &str { + "bcm2835-mbox" + } +} + +impl Bcm2835Mbox { + const RAM_TO_GPU_OFFSET: u32 = 0x40000000; + + pub unsafe fn call_raw(&self, buffer: PhysicalAddress, channel: u32) -> Result<(), Error> { + log::info!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}"); + + let address = buffer + .try_into_u32() + .inspect_err(|_| log::error!("bcm2835-mbox: invalid physical address {buffer:#x}")) + .map_err(|_| Error::InvalidArgument)?; + if address >= Self::RAM_TO_GPU_OFFSET { + log::error!("bcm2835-mbox: RAM address {buffer:#x} is too high"); + return Err(Error::InvalidArgument); + } + let address = address + Self::RAM_TO_GPU_OFFSET; + + let regs = self.regs.lock(); + while regs.STATUS.matches_all(MboxStatus::FULL::SET) { + core::hint::spin_loop(); + } + + let val = MboxValue::ADDRESS.val(address >> 4) + MboxValue::CHANNEL.val(channel); + + regs.WRITE.write(val); + + loop { + while regs.STATUS.matches_all(MboxStatus::EMPTY::SET) { + core::hint::spin_loop(); + } + + let val = regs.READ.extract(); + + if val.matches_all(MboxValue::CHANNEL.val(channel)) { + return Ok(()); + } + } + } + + pub fn configure_framebuffer( + &self, + mode_info: &DisplayMode, + ) -> Result { + #[derive(Debug, Clone, Copy)] + #[repr(C)] + struct FramebufferRequest { + physical_width: u32, + physical_height: u32, + virtual_width: u32, + virtual_height: u32, + pitch: u32, + bpp: u32, + x_offset: u32, + y_offset: u32, + address: u32, + size: u32, + } + + let mut buffer = PageBox::new(UnsafeCell::new(FramebufferRequest { + physical_width: mode_info.width, + physical_height: mode_info.height, + virtual_width: mode_info.width, + virtual_height: mode_info.height, + pitch: 0, + bpp: 32, + x_offset: 0, + y_offset: 0, + address: 0, + size: 0, + }))?; + + // TODO flush cache for buffer + unsafe { self.call_raw(buffer.as_physical_address(), 0x01) }?; + + let buffer = buffer.get_mut(); + + log::info!("bcm2835-fb: mode set response: {buffer:#?}"); + + if buffer.address == 0 || buffer.size == 0 || buffer.pitch == 0 { + // Request failed + log::error!("bcm2835-fb: mode set failed"); + return Err(Error::InvalidArgument); + } + + let address = buffer.address & (Bcm2835Mbox::RAM_TO_GPU_OFFSET - 1); + + Ok(FramebufferResponse { + physical_address: address, + size: buffer.size, + pitch: buffer.pitch, + }) + } +} + +impl Bcm2835Framebuffer { + pub fn setup(mbox: &Arc) -> Result<(), Error> { + const WIDTH: u32 = 1024; + const HEIGHT: u32 = 768; + + let mode = DisplayMode { + index: 0, + width: WIDTH, + height: HEIGHT, + frames_per_second: 60, + pixel_format: PixelFormat::R8G8B8A8, + }; + + let framebuffer = mbox.configure_framebuffer(&mode)?; + + let fb = Arc::new(Self { + mode, + base: PhysicalAddress::from_u32(framebuffer.physical_address), + size: framebuffer.size as usize, + pitch: framebuffer.pitch as usize, + }); + + DEVICE_REGISTRY.display.register(fb, false)?; + + Ok(()) + } +} + +impl Device for Bcm2835Framebuffer { + unsafe fn init(self: Arc, _cx: DeviceInitContext) -> Result<(), Error> { + unreachable!() + } + + fn display_name(&self) -> &str { + "bcm2835-fb" + } +} + +impl PageProvider for Bcm2835Framebuffer { + fn get_page(&self, offset: u64) -> Result { + let offset = offset as usize; + if offset + L3::SIZE > self.size { + log::warn!( + "Tried to map offset {:#x}, but size is {:#x}", + offset, + self.size + ); + Err(Error::InvalidMemoryOperation) + } else { + let page = self.base.add(offset); + Ok(VirtualPage::Immediate(page)) + } + } + + fn clone_page( + &self, + _offset: u64, + _src_phys: PhysicalAddress, + _src_attrs: MapAttributes, + ) -> Result { + unimplemented!() + } + + fn release_page( + &self, + _offset: u64, + _phys: PhysicalAddress, + _dirty: bool, + ) -> Result<(), Error> { + todo!() + } + + fn ondemand_fetch(&self, _offset: u64) -> Result { + unimplemented!() + } +} + +impl DisplayDevice for Bcm2835Framebuffer { + fn lock(&self, _owner: DisplayOwner) -> Result<(), Error> { + Ok(()) + } + + fn unlock(&self, _owner: DisplayOwner) -> Result<(), Error> { + Ok(()) + } + + fn synchronize(&self) -> Result<(), Error> { + Ok(()) + } + + fn driver_flags(&self) -> DriverFlags { + DriverFlags::empty() + } + + fn set_display_mode(&self, _index: u32, _double_buffer: bool) -> Result<(), Error> { + Err(Error::NotImplemented) + } + + fn active_framebuffer(&self) -> Result { + Ok(FramebufferInfo { + base: self.base, + kernel_base: None, + stride: self.pitch, + size: self.size, + }) + } + + fn active_display_mode(&self) -> Option { + Some(self.mode) + } + + fn query_display_modes(&self, modes: &mut [MaybeUninit]) -> Result { + if modes.is_empty() { + return Err(Error::BufferTooSmall); + } + modes[0].write(self.mode); + Ok(1) + } + + fn active_framebuffers( + &self, + output: &mut [MaybeUninit], + ) -> Result { + if output.is_empty() { + return Err(1); + } + output[0].write(FramebufferInfo { + base: self.base, + kernel_base: None, + stride: self.pitch, + size: self.size, + }); + Ok(1) + } +} + +device_tree_driver! { + compatible: ["brcm,bcm2835-mbox"], + driver: { + fn probe(&self, node: &Arc, context: &mut ProbeContext) -> Option> { + let base = node.map_base(context, 0)?; + // TODO interrupts + let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) } + .inspect_err(|e| log::error!("bcm2835-mbox: mapping failed: {e:?}")) + .ok()?; + let mbox = Arc::new(Bcm2835Mbox { + regs: IrqSafeSpinlock::new(regs) + }); + // TODO there's no fdt node for a framebuffer, so it's hardcoded as a child of the mbox + if let Err(error) = Bcm2835Framebuffer::setup(&mbox) { + log::error!("bcm2835-mbox: framebuffer setup error: {error:?}"); + } + + Some(mbox) + } + } +} diff --git a/kernel/src/device/mbox/mod.rs b/kernel/src/device/mbox/mod.rs new file mode 100644 index 00000000..53866a66 --- /dev/null +++ b/kernel/src/device/mbox/mod.rs @@ -0,0 +1,4 @@ +//! Mailbox interfaces + +#[cfg(any(rust_analyzer, target_arch = "aarch64"))] +mod bcm2835_mbox; diff --git a/kernel/src/device/mod.rs b/kernel/src/device/mod.rs index 04b4f48b..ff1d1bac 100644 --- a/kernel/src/device/mod.rs +++ b/kernel/src/device/mod.rs @@ -7,6 +7,7 @@ pub mod bus; pub mod clock; pub mod display; pub mod interrupt; +pub mod mbox; pub mod power; pub mod serial; // pub mod timer; diff --git a/userspace/graphics/colors/src/sys/yggdrasil.rs b/userspace/graphics/colors/src/sys/yggdrasil.rs index 50543043..cf350400 100644 --- a/userspace/graphics/colors/src/sys/yggdrasil.rs +++ b/userspace/graphics/colors/src/sys/yggdrasil.rs @@ -45,8 +45,7 @@ struct Display<'a> { width: usize, height: usize, - // TODO use those - _stride: usize, + stride: usize, _size: usize, } @@ -82,7 +81,7 @@ impl DisplaySurface<'_, '_> { let h = cmp::min(src_h, dst_h); for y in 0..h { - let dst_offset = (y + src.1 + dst.1) * self.display.width + dst.0 + src.0; + let dst_offset = (y + src.1 + dst.1) * self.display.stride + dst.0 + src.0; let src_offset = (y + src.1) * src_stride + src.0; let src_chunk = &source[src_offset..src_offset + w]; @@ -194,10 +193,11 @@ impl Display<'_> { let width = framebuffer.width as usize; let height = framebuffer.height as usize; + let stride = framebuffer.stride / size_of::(); let mut mapping = FileMapping::map(file, framebuffer.size, true)?; let data = unsafe { - std::slice::from_raw_parts_mut(mapping.as_mut_ptr() as *mut u32, width * height) + std::slice::from_raw_parts_mut(mapping.as_mut_ptr() as *mut u32, height * stride) }; Ok(Self { @@ -206,7 +206,7 @@ impl Display<'_> { width, height, - _stride: framebuffer.stride / size_of::(), + stride, _size: framebuffer.size / size_of::(), }) }