From 6ba9e34f770edb7988adf615b47a84e570e69975 Mon Sep 17 00:00:00 2001 From: equation314 Date: Sun, 16 Dec 2018 20:55:04 +0800 Subject: [PATCH] aarch64/fb: add framebuffer initialization --- kernel/src/arch/aarch64/board/raspi3/fb.rs | 190 ++++++++++++++++++ .../src/arch/aarch64/board/raspi3/mailbox.rs | 93 ++++++++- kernel/src/arch/aarch64/board/raspi3/mod.rs | 8 +- .../src/arch/aarch64/board/raspi3/serial.rs | 10 +- kernel/src/arch/aarch64/mod.rs | 6 +- kernel/src/lib.rs | 2 + 6 files changed, 297 insertions(+), 12 deletions(-) create mode 100644 kernel/src/arch/aarch64/board/raspi3/fb.rs diff --git a/kernel/src/arch/aarch64/board/raspi3/fb.rs b/kernel/src/arch/aarch64/board/raspi3/fb.rs new file mode 100644 index 0000000..00ff2da --- /dev/null +++ b/kernel/src/arch/aarch64/board/raspi3/fb.rs @@ -0,0 +1,190 @@ +//! Framebuffer + +use super::mailbox; +use alloc::string::String; +use core::fmt; +use lazy_static::lazy_static; +use log::*; +use once::*; +use spin::Mutex; + +/// Framebuffer information +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct FramebufferInfo { + /// visible width + pub xres: u32, + /// visible height + pub yres: u32, + /// virtual width + pub xres_virtual: u32, + /// virtual height + pub yres_virtual: u32, + /// virtual offset x + pub xoffset: u32, + /// virtual offset y + pub yoffset: u32, + + /// bits per pixel + pub depth: u32, + /// bytes per line + pub pitch: u32, + + /// bus address, starts from 0xC0000000/0x40000000 + /// (see https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes) + pub bus_addr: u32, + /// screen buffer size + pub screen_size: u32, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +#[allow(non_camel_case_types)] +pub enum ColorFormat { + BGR565 = 16, + RGBA8888 = 32, +} +use self::ColorFormat::*; + +#[repr(C)] +union ColorBuffer { + base_addr: u32, + buf16: &'static mut [u16], + buf32: &'static mut [u32], +} + +impl ColorBuffer { + fn new(color_format: ColorFormat, bus_addr: u32, size: u32) -> ColorBuffer { + unsafe { + match color_format { + BGR565 => ColorBuffer { + buf16: core::slice::from_raw_parts_mut( + bus_addr as *mut u16, + (size / 2) as usize, + ), + }, + RGBA8888 => ColorBuffer { + buf32: core::slice::from_raw_parts_mut( + bus_addr as *mut u32, + (size / 4) as usize, + ), + }, + } + } + } + + #[inline] + fn read16(&self, index: u32) -> u16 { + unsafe { self.buf16[index as usize] } + } + + #[inline] + fn read32(&self, index: u32) -> u32 { + unsafe { self.buf32[index as usize] } + } + + #[inline] + fn write16(&mut self, index: u32, pixel: u16) { + unsafe { self.buf16[index as usize] = pixel } + } + + #[inline] + fn write32(&mut self, index: u32, pixel: u32) { + unsafe { self.buf32[index as usize] = pixel } + } +} + +/// Frambuffer structure +pub struct Framebuffer { + pub fb_info: FramebufferInfo, + pub color_format: ColorFormat, + buf: ColorBuffer, +} + +impl fmt::Debug for Framebuffer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut f = f.debug_struct("Framebuffer"); + f.field("fb_info", &self.fb_info); + f.field("color_format", &self.color_format); + f.field("base_addr", unsafe { &self.buf.base_addr }); + f.finish() + } +} + +impl Framebuffer { + fn new(width: u32, height: u32, depth: u32) -> Result { + assert_has_not_been_called!("Framebuffer::new must be called only once"); + + let (width, height) = if width == 0 || height == 0 { + mailbox::framebuffer_get_physical_size()? + } else { + (width, height) + }; + let depth = if depth == 0 { + mailbox::framebuffer_get_depth()? + } else { + depth + }; + + let info = mailbox::framebuffer_alloc(width, height, depth)?; + let color_format = match info.depth { + 16 => BGR565, + 32 => RGBA8888, + _ => Err(format!("unsupported color depth {}", info.depth))?, + }; + + if info.bus_addr == 0 || info.screen_size == 0 { + Err(format!("mailbox call returned an invalid address/size"))?; + } + if info.pitch == 0 || info.pitch != info.xres * info.depth / 8 { + Err(format!( + "mailbox call returned an invalid pitch value {}", + info.pitch + ))?; + } + + let paddr = info.bus_addr & !0xC0000000; + Ok(Framebuffer { + buf: ColorBuffer::new(color_format, paddr, info.screen_size), + color_format, + fb_info: info, + }) + } + + #[inline] + pub fn base_addr(&self) -> u32 { + unsafe { self.buf.base_addr } + } + + #[inline] + pub fn read(&self, x: u32, y: u32) -> u32 { + match self.color_format { + BGR565 => self.buf.read16(y * self.fb_info.xres + x) as u32, + RGBA8888 => self.buf.read32(y * self.fb_info.xres + x), + } + } + + #[inline] + pub fn write(&mut self, x: u32, y: u32, pixel: u32) { + match self.color_format { + BGR565 => self.buf.write16(y * self.fb_info.xres + x, pixel as u16), + RGBA8888 => self.buf.write32(y * self.fb_info.xres + x, pixel), + } + } +} + +lazy_static! { + pub static ref FRAME_BUFFER: Mutex> = Mutex::new(None); +} + +/// Initialize framebuffer +pub fn init() { + match Framebuffer::new(0, 0, 0) { + Ok(fb) => { + let info = fb.fb_info; + info!("framebuffer: init end\n{:#x?}", fb); + *FRAME_BUFFER.lock() = Some(fb); + } + Err(err) => error!("framebuffer init failed: {}", err), + } +} diff --git a/kernel/src/arch/aarch64/board/raspi3/mailbox.rs b/kernel/src/arch/aarch64/board/raspi3/mailbox.rs index 83e1557..6efb670 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mailbox.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mailbox.rs @@ -2,8 +2,10 @@ //! //! (ref: https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface) +use super::fb::FramebufferInfo; use bcm2837::mailbox::{Mailbox, MailboxChannel}; use lazy_static::lazy_static; +use alloc::string::String; use core::mem; use spin::Mutex; use aarch64::barrier; @@ -16,6 +18,12 @@ lazy_static! { pub struct PropertyMailboxError(u32); pub type PropertyMailboxResult = Result; +impl From for String { + fn from(error: PropertyMailboxError) -> Self { + format!("{:x?}", error) + } +} + /// Buffer request/response code. /// Copied from `linux/include/soc/bcm2835/raspberrypi-firmware.h` #[repr(u32)] @@ -167,11 +175,11 @@ struct Align16(PropertyMailboxRequest); /// Pack a sequence of concatenated tags into a request, and send the address /// to the mailbox. -/// Returns PropertyMailboxResult. +/// Returns `PropertyMailboxResult`. macro_rules! send_request { ($tags: ident) => {{ let req = Align16(PropertyMailboxRequest { - buf_size: mem::size_of_val(&$tags) as u32, + buf_size: mem::size_of_val(&$tags) as u32 + 12, req_resp_code: RPI_FIRMWARE_STATUS_REQUEST, buf: $tags, end_tag: RPI_FIRMWARE_PROPERTY_END, @@ -179,8 +187,9 @@ macro_rules! send_request { unsafe { barrier::wmb() } { + let addr = &req as *const _ as u32; let mut mbox = MAILBOX.lock(); - mbox.write(MailboxChannel::Property, &req as *const _ as u32); + mbox.write(MailboxChannel::Property, addr); mbox.read(MailboxChannel::Property); } unsafe { barrier::rmb() } @@ -193,7 +202,7 @@ macro_rules! send_request { } /// Send a tag to mailbox. Will call `send_request!`. -/// Returns PropertyMailboxResult. +/// Returns `PropertyMailboxResult`. macro_rules! send_one_tag { ($id: expr, [$($arg: expr),*]) => {{ let buf = [$($arg),*]; @@ -264,3 +273,79 @@ pub fn framebuffer_set_virtual_offset(xoffset: u32, yoffset: u32) -> PropertyMai )?; Ok((ret[0], ret[1])) } + +/// Allocate framebuffer on GPU and try to set width/height/depth. +/// Returns `FramebufferInfo`. +pub fn framebuffer_alloc(width: u32, height: u32, depth: u32) -> PropertyMailboxResult { + #[repr(C, packed)] + #[derive(Debug)] + struct FramebufferAllocTag { + set_physical_size: PropertyMailboxTag<[u32; 2]>, + set_virtual_size: PropertyMailboxTag<[u32; 2]>, + set_depth: PropertyMailboxTag<[u32; 1]>, + set_virtual_offset: PropertyMailboxTag<[u32; 2]>, + allocate: PropertyMailboxTag<[u32; 2]>, + get_pitch: PropertyMailboxTag<[u32; 1]>, + } + + let tags = FramebufferAllocTag { + // Set physical (buffer) width/height. Returns `(width, height)` in pixel. + set_physical_size: PropertyMailboxTag { + id: RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT, + buf_size: 8, + req_resp_size: 0, + buf: [width, height], + }, + // Set virtual (buffer) width/height. Returns `(width, height)` in pixel. + set_virtual_size: PropertyMailboxTag { + id: RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT, + buf_size: 8, + req_resp_size: 0, + buf: [width, height], + }, + // Set depth; Returns bits per pixel. + set_depth: PropertyMailboxTag { + id: RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH, + buf_size: 4, + req_resp_size: 0, + buf: [depth], + }, + // Set virtual offset. Returns `(X, Y)` in pixel. + set_virtual_offset: PropertyMailboxTag { + id: RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET, + buf_size: 8, + req_resp_size: 0, + buf: [0, 0], + }, + // Allocate buffer. Returns `(base_address, size)` in bytes. + allocate: PropertyMailboxTag { + id: RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE, + buf_size: 8, + req_resp_size: 0, + buf: [0x1000, 0], + }, + // Get pitch. Return bytes per line. + get_pitch: PropertyMailboxTag { + id: RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH, + buf_size: 4, + req_resp_size: 0, + buf: [0], + }, + }; + + let ret = send_request!(tags)?; + Ok(FramebufferInfo { + xres: ret.set_physical_size.buf[0], + yres: ret.set_physical_size.buf[1], + xres_virtual: ret.set_virtual_size.buf[0], + yres_virtual: ret.set_virtual_size.buf[1], + xoffset: ret.set_virtual_offset.buf[0], + yoffset: ret.set_virtual_offset.buf[1], + + depth: ret.set_depth.buf[0], + pitch: ret.get_pitch.buf[0], + + bus_addr: ret.allocate.buf[0], + screen_size: ret.allocate.buf[1], + }) +} diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index f62aad2..2bbee12 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -2,6 +2,7 @@ use once::*; +pub mod fb; pub mod irq; pub mod timer; pub mod serial; @@ -10,16 +11,17 @@ pub mod mailbox; pub const IO_REMAP_BASE: usize = bcm2837::IO_BASE; pub const IO_REMAP_END: usize = 0x40001000; -/// Some initializations must be done before other initializations. -pub fn init_early() { +/// Initialize serial port before other initializations. +pub fn init_serial_early() { assert_has_not_been_called!("board::init must be called only once"); - serial::SERIAL_PORT.lock().init(); + serial::init(); println!("Hello Raspberry Pi!"); } /// Initialize raspi3 drivers pub fn init_driver() { + fb::init(); timer::init(); } diff --git a/kernel/src/arch/aarch64/board/raspi3/serial.rs b/kernel/src/arch/aarch64/board/raspi3/serial.rs index 74ed5dd..d74432a 100644 --- a/kernel/src/arch/aarch64/board/raspi3/serial.rs +++ b/kernel/src/arch/aarch64/board/raspi3/serial.rs @@ -2,6 +2,7 @@ use bcm2837::mini_uart::{MiniUart, MiniUartInterruptId}; use lazy_static::lazy_static; use core::fmt; use spin::Mutex; +use once::*; /// Struct to get a global SerialPort interface pub struct SerialPort { @@ -22,6 +23,8 @@ impl SerialPort { /// Init a newly created SerialPort, can only be called once. pub fn init(&mut self) { + assert_has_not_been_called!("SerialPort::init must be called only once"); + self.mu.init(); super::irq::register_irq(super::irq::Interrupt::Aux, handle_serial_irq); } @@ -78,6 +81,11 @@ fn handle_serial_irq() { } } -lazy_static!{ +lazy_static! { pub static ref SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); } + + +pub fn init() { + SERIAL_PORT.lock().init(); +} diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index 3edf8cb..ecfc34d 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -18,15 +18,13 @@ global_asm!(include_str!("boot/boot.S")); #[no_mangle] // don't mangle the name of this function pub extern "C" fn rust_main() -> ! { memory::init_mmu_early(); // Enable mmu and paging + board::init_serial_early(); crate::logging::init(); - - board::init_early(); - println!("{}", LOGO); - interrupt::init(); memory::init(); driver::init(); + println!("{}", LOGO); crate::process::init(); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 6513ac9..f6d938e 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,6 +1,7 @@ #![feature(lang_items)] #![feature(alloc)] #![feature(naked_functions)] +#![feature(untagged_unions)] #![feature(asm)] #![feature(optin_builtin_traits)] #![feature(panic_info_message)] @@ -8,6 +9,7 @@ #![no_std] // just keep it ... +#[macro_use] extern crate alloc; pub use crate::process::{processor, new_kernel_context};