# Conflicts: # kernel/Makefile # kernel/src/arch/aarch64/board/raspi3/irq.rs # kernel/src/arch/aarch64/memory.rs # kernel/src/arch/riscv32/paging.rs # kernel/src/arch/x86_64/paging.rsmaster
@ -0,0 +1,80 @@
|
|||||||
|
use crate::IO_BASE;
|
||||||
|
use volatile::{ReadOnly, Volatile, WriteOnly};
|
||||||
|
|
||||||
|
/// The base address for the `MU` registers.
|
||||||
|
const MAILBOX_BASE: usize = IO_BASE + 0xB000 + 0x880;
|
||||||
|
|
||||||
|
/// Available mailbox channels
|
||||||
|
///
|
||||||
|
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum MailboxChannel {
|
||||||
|
Framebuffer = 1,
|
||||||
|
Property = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from mailbox status register (MAILx_STA).
|
||||||
|
#[repr(u32)]
|
||||||
|
enum MailboxStatus {
|
||||||
|
MailboxEmpty = 1 << 30,
|
||||||
|
MailboxFull = 1 << 31,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mailbox registers. We basically only support mailbox 0 & 1. We
|
||||||
|
/// deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See
|
||||||
|
/// BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about
|
||||||
|
/// the placement of memory barriers.
|
||||||
|
///
|
||||||
|
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct Registers {
|
||||||
|
MAIL0_RD: ReadOnly<u32>, // 0x00
|
||||||
|
__reserved0: [u32; 3],
|
||||||
|
MAIL0_POL: ReadOnly<u32>, // 0x10
|
||||||
|
MAIL0_SND: ReadOnly<u32>, // 0x14
|
||||||
|
MAIL0_STA: ReadOnly<u32>, // 0x18
|
||||||
|
MAIL0_CNF: Volatile<u32>, // 0x1c
|
||||||
|
|
||||||
|
MAIL1_WRT: WriteOnly<u32>, // 0x20
|
||||||
|
__reserved1: [u32; 3],
|
||||||
|
_MAIL1_POL: ReadOnly<u32>, // 0x30
|
||||||
|
_MAIL1_SND: ReadOnly<u32>, // 0x34
|
||||||
|
MAIL1_STA: ReadOnly<u32>, // 0x38
|
||||||
|
_MAIL1_CNF: Volatile<u32>, // 0x3c
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Raspberry Pi's mailbox.
|
||||||
|
///
|
||||||
|
/// (ref: https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes)
|
||||||
|
pub struct Mailbox {
|
||||||
|
registers: &'static mut Registers,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mailbox {
|
||||||
|
/// Returns a new instance of `Mailbox`.
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Mailbox {
|
||||||
|
Mailbox {
|
||||||
|
registers: unsafe { &mut *(MAILBOX_BASE as *mut Registers) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from the requested channel of mailbox 0.
|
||||||
|
pub fn read(&self, channel: MailboxChannel) -> u32 {
|
||||||
|
loop {
|
||||||
|
while self.registers.MAIL0_STA.read() & (MailboxStatus::MailboxEmpty as u32) != 0 {}
|
||||||
|
let data = self.registers.MAIL0_RD.read();
|
||||||
|
if data & 0xF == channel as u32 {
|
||||||
|
return data & !0xF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write to the requested channel of mailbox 1.
|
||||||
|
pub fn write(&mut self, channel: MailboxChannel, data: u32) {
|
||||||
|
while self.registers.MAIL1_STA.read() & (MailboxStatus::MailboxFull as u32) != 0 {}
|
||||||
|
self.registers.MAIL1_WRT.write((data & !0xF) | (channel as u32));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
# Rust OS 在 Raspberry Pi 3 上的移植
|
||||||
|
|
||||||
|
计54 贾越凯 2015011335
|
||||||
|
|
||||||
|
计54 寇明阳 2015011318
|
||||||
|
|
||||||
|
计55 孔彦 2015011349
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
* [概述](overview.md)
|
||||||
|
* [环境配置](environment.md)
|
||||||
|
* [启动与初始化](boot.md)
|
||||||
|
* [中断与异常](interrupt.md)
|
||||||
|
* [内存管理](memory.md)
|
||||||
|
* [上下文切换](context.md)
|
||||||
|
* [设备驱动](drivers.md)
|
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 342 KiB |
After Width: | Height: | Size: 163 KiB |
After Width: | Height: | Size: 182 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 30 KiB |
@ -0,0 +1,238 @@
|
|||||||
|
//! 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)]
|
||||||
|
pub enum ColorDepth {
|
||||||
|
ColorDepth16 = 16,
|
||||||
|
ColorDepth32 = 32,
|
||||||
|
}
|
||||||
|
use self::ColorDepth::*;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
union ColorBuffer {
|
||||||
|
base_addr: usize,
|
||||||
|
buf16: &'static mut [u16],
|
||||||
|
buf32: &'static mut [u32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorBuffer {
|
||||||
|
fn new(color_depth: ColorDepth, bus_addr: u32, size: u32) -> ColorBuffer {
|
||||||
|
unsafe {
|
||||||
|
match color_depth {
|
||||||
|
ColorDepth16 => ColorBuffer {
|
||||||
|
buf16: core::slice::from_raw_parts_mut(
|
||||||
|
bus_addr as *mut u16,
|
||||||
|
(size / 2) as usize,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ColorDepth32 => 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_depth: ColorDepth,
|
||||||
|
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_depth", &self.color_depth);
|
||||||
|
f.field("base_addr", unsafe { &self.buf.base_addr });
|
||||||
|
f.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Framebuffer {
|
||||||
|
fn new(width: u32, height: u32, depth: u32) -> Result<Framebuffer, String> {
|
||||||
|
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_depth = match info.depth {
|
||||||
|
16 => ColorDepth16,
|
||||||
|
32 => ColorDepth32,
|
||||||
|
_ => 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
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::arch::memory;
|
||||||
|
let paddr = info.bus_addr & !0xC0000000;
|
||||||
|
let vaddr = memory::ioremap(paddr as usize, info.screen_size as usize, "fb") as u32;
|
||||||
|
if vaddr == 0 {
|
||||||
|
Err(format!(
|
||||||
|
"cannot remap memory range [{:#x?}..{:#x?}]",
|
||||||
|
paddr,
|
||||||
|
paddr + info.screen_size
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
Ok(Framebuffer {
|
||||||
|
buf: ColorBuffer::new(color_depth, vaddr, info.screen_size),
|
||||||
|
color_depth,
|
||||||
|
fb_info: info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn base_addr(&self) -> usize {
|
||||||
|
unsafe { self.buf.base_addr }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read pixel at `(x, y)`.
|
||||||
|
#[inline]
|
||||||
|
pub fn read(&self, x: u32, y: u32) -> u32 {
|
||||||
|
match self.color_depth {
|
||||||
|
ColorDepth16 => self.buf.read16(y * self.fb_info.xres + x) as u32,
|
||||||
|
ColorDepth32 => self.buf.read32(y * self.fb_info.xres + x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write pixel at `(x, y)`.
|
||||||
|
#[inline]
|
||||||
|
pub fn write(&mut self, x: u32, y: u32, pixel: u32) {
|
||||||
|
match self.color_depth {
|
||||||
|
ColorDepth16 => self.buf.write16(y * self.fb_info.xres + x, pixel as u16),
|
||||||
|
ColorDepth32 => self.buf.write32(y * self.fb_info.xres + x, pixel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy buffer `[src_off .. src_off + size]` to `[dst_off .. dst_off + size]`.
|
||||||
|
/// `dst_off`, `src_off` and `size` must be aligned with `usize`.
|
||||||
|
pub fn copy(&mut self, dst_off: usize, src_off: usize, size: usize) {
|
||||||
|
const USIZE: usize = core::mem::size_of::<usize>();
|
||||||
|
let mut dst = self.base_addr() + dst_off;
|
||||||
|
let mut src = self.base_addr() + src_off;
|
||||||
|
let src_end = src + size;
|
||||||
|
while src < src_end {
|
||||||
|
unsafe { *(dst as *mut usize) = *(src as *mut usize) }
|
||||||
|
dst += USIZE;
|
||||||
|
src += USIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill buffer `[offset .. offset + size]` with `pixel`.
|
||||||
|
/// `offset` and `size` must be aligned with `usize`.
|
||||||
|
pub fn fill(&mut self, offset: usize, size: usize, pixel: u32) {
|
||||||
|
const USIZE: usize = core::mem::size_of::<usize>();
|
||||||
|
let mut value: usize = 0;
|
||||||
|
let repeat = USIZE * 8 / self.fb_info.depth as usize;
|
||||||
|
let mask = ((1u64 << self.fb_info.depth) - 1) as usize;
|
||||||
|
for _i in 0..repeat {
|
||||||
|
value <<= self.fb_info.depth;
|
||||||
|
value += pixel as usize & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut start = self.base_addr() + offset;
|
||||||
|
let end = start + size;
|
||||||
|
while start < end {
|
||||||
|
unsafe { *(start as *mut usize) = value }
|
||||||
|
start += USIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill the entire buffer with `0`.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.fill(0, self.fb_info.screen_size as usize, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref FRAME_BUFFER: Mutex<Option<Framebuffer>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize framebuffer
|
||||||
|
pub fn init() {
|
||||||
|
match Framebuffer::new(0, 0, 0) {
|
||||||
|
Ok(fb) => {
|
||||||
|
info!("framebuffer: init end\n{:#x?}", fb);
|
||||||
|
*FRAME_BUFFER.lock() = Some(fb);
|
||||||
|
}
|
||||||
|
Err(err) => warn!("framebuffer init failed: {}", err),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,353 @@
|
|||||||
|
//! Mailbox property interface
|
||||||
|
//!
|
||||||
|
//! (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::asm;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref MAILBOX: Mutex<Mailbox> = Mutex::new(Mailbox::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PropertyMailboxError(u32);
|
||||||
|
pub type PropertyMailboxResult<T> = Result<T, PropertyMailboxError>;
|
||||||
|
|
||||||
|
impl From<PropertyMailboxError> 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)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
enum PropertyMailboxStatus {
|
||||||
|
RPI_FIRMWARE_STATUS_REQUEST = 0,
|
||||||
|
RPI_FIRMWARE_STATUS_SUCCESS = 0x80000000,
|
||||||
|
RPI_FIRMWARE_STATUS_ERROR = 0x80000001,
|
||||||
|
}
|
||||||
|
use self::PropertyMailboxStatus::*;
|
||||||
|
|
||||||
|
/// Tag identifier.
|
||||||
|
/// Copied from `linux/include/soc/bcm2835/raspberrypi-firmware.h`
|
||||||
|
#[repr(u32)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
enum PropertyMailboxTagId {
|
||||||
|
RPI_FIRMWARE_PROPERTY_END = 0,
|
||||||
|
RPI_FIRMWARE_GET_FIRMWARE_REVISION = 0x00000001,
|
||||||
|
|
||||||
|
RPI_FIRMWARE_SET_CURSOR_INFO = 0x00008010,
|
||||||
|
RPI_FIRMWARE_SET_CURSOR_STATE = 0x00008011,
|
||||||
|
|
||||||
|
RPI_FIRMWARE_GET_BOARD_MODEL = 0x00010001,
|
||||||
|
RPI_FIRMWARE_GET_BOARD_REVISION = 0x00010002,
|
||||||
|
RPI_FIRMWARE_GET_BOARD_MAC_ADDRESS = 0x00010003,
|
||||||
|
RPI_FIRMWARE_GET_BOARD_SERIAL = 0x00010004,
|
||||||
|
RPI_FIRMWARE_GET_ARM_MEMORY = 0x00010005,
|
||||||
|
RPI_FIRMWARE_GET_VC_MEMORY = 0x00010006,
|
||||||
|
RPI_FIRMWARE_GET_CLOCKS = 0x00010007,
|
||||||
|
RPI_FIRMWARE_GET_POWER_STATE = 0x00020001,
|
||||||
|
RPI_FIRMWARE_GET_TIMING = 0x00020002,
|
||||||
|
RPI_FIRMWARE_SET_POWER_STATE = 0x00028001,
|
||||||
|
RPI_FIRMWARE_GET_CLOCK_STATE = 0x00030001,
|
||||||
|
RPI_FIRMWARE_GET_CLOCK_RATE = 0x00030002,
|
||||||
|
RPI_FIRMWARE_GET_VOLTAGE = 0x00030003,
|
||||||
|
RPI_FIRMWARE_GET_MAX_CLOCK_RATE = 0x00030004,
|
||||||
|
RPI_FIRMWARE_GET_MAX_VOLTAGE = 0x00030005,
|
||||||
|
RPI_FIRMWARE_GET_TEMPERATURE = 0x00030006,
|
||||||
|
RPI_FIRMWARE_GET_MIN_CLOCK_RATE = 0x00030007,
|
||||||
|
RPI_FIRMWARE_GET_MIN_VOLTAGE = 0x00030008,
|
||||||
|
RPI_FIRMWARE_GET_TURBO = 0x00030009,
|
||||||
|
RPI_FIRMWARE_GET_MAX_TEMPERATURE = 0x0003000a,
|
||||||
|
RPI_FIRMWARE_GET_STC = 0x0003000b,
|
||||||
|
RPI_FIRMWARE_ALLOCATE_MEMORY = 0x0003000c,
|
||||||
|
RPI_FIRMWARE_LOCK_MEMORY = 0x0003000d,
|
||||||
|
RPI_FIRMWARE_UNLOCK_MEMORY = 0x0003000e,
|
||||||
|
RPI_FIRMWARE_RELEASE_MEMORY = 0x0003000f,
|
||||||
|
RPI_FIRMWARE_EXECUTE_CODE = 0x00030010,
|
||||||
|
RPI_FIRMWARE_EXECUTE_QPU = 0x00030011,
|
||||||
|
RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012,
|
||||||
|
RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014,
|
||||||
|
RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020,
|
||||||
|
RPI_FIRMWARE_GET_CUSTOMER_OTP = 0x00030021,
|
||||||
|
RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030,
|
||||||
|
RPI_FIRMWARE_GET_THROTTLED = 0x00030046,
|
||||||
|
RPI_FIRMWARE_GET_CLOCK_MEASURED = 0x00030047,
|
||||||
|
RPI_FIRMWARE_NOTIFY_REBOOT = 0x00030048,
|
||||||
|
RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001,
|
||||||
|
RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002,
|
||||||
|
RPI_FIRMWARE_SET_VOLTAGE = 0x00038003,
|
||||||
|
RPI_FIRMWARE_SET_TURBO = 0x00038009,
|
||||||
|
RPI_FIRMWARE_SET_CUSTOMER_OTP = 0x00038021,
|
||||||
|
RPI_FIRMWARE_SET_DOMAIN_STATE = 0x00038030,
|
||||||
|
RPI_FIRMWARE_GET_GPIO_STATE = 0x00030041,
|
||||||
|
RPI_FIRMWARE_SET_GPIO_STATE = 0x00038041,
|
||||||
|
RPI_FIRMWARE_SET_SDHOST_CLOCK = 0x00038042,
|
||||||
|
RPI_FIRMWARE_GET_GPIO_CONFIG = 0x00030043,
|
||||||
|
RPI_FIRMWARE_SET_GPIO_CONFIG = 0x00038043,
|
||||||
|
RPI_FIRMWARE_GET_PERIPH_REG = 0x00030045,
|
||||||
|
RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045,
|
||||||
|
RPI_FIRMWARE_GET_POE_HAT_VAL = 0x00030049,
|
||||||
|
RPI_FIRMWARE_SET_POE_HAT_VAL = 0x00030050,
|
||||||
|
|
||||||
|
/* Dispmanx TAGS */
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_BLANK = 0x00040002,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_DEPTH = 0x00040005,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_PIXEL_ORDER = 0x00040006,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_ALPHA_MODE = 0x00040007,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH = 0x00040008,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET = 0x00040009,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF = 0x0004000f,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF = 0x00040010,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_RELEASE = 0x00048001,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH = 0x00044005,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_PIXEL_ORDER = 0x00044006,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_ALPHA_MODE = 0x00044007,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET = 0x00044009,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN = 0x0004400a,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE = 0x0004400b,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_TEST_VSYNC = 0x0004400e,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH = 0x00048005,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_PIXEL_ORDER = 0x00048006,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_ALPHA_MODE = 0x00048007,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET = 0x00048009,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF = 0x0004801f,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF = 0x00048020,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC = 0x0004800e,
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f,
|
||||||
|
|
||||||
|
RPI_FIRMWARE_VCHIQ_INIT = 0x00048010,
|
||||||
|
|
||||||
|
RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001,
|
||||||
|
RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001,
|
||||||
|
}
|
||||||
|
use self::PropertyMailboxTagId::*;
|
||||||
|
|
||||||
|
/// A property mailbox tag.
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(safe_packed_borrows)]
|
||||||
|
struct PropertyMailboxTag<T: Sized> {
|
||||||
|
id: PropertyMailboxTagId,
|
||||||
|
buf_size: u32,
|
||||||
|
req_resp_size: u32,
|
||||||
|
buf: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A request that contained a sequence of concatenated tags. The response
|
||||||
|
/// overwrites the request.
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(safe_packed_borrows)]
|
||||||
|
struct PropertyMailboxRequest<T: Sized> {
|
||||||
|
buf_size: u32,
|
||||||
|
req_resp_code: PropertyMailboxStatus,
|
||||||
|
buf: T,
|
||||||
|
end_tag: PropertyMailboxTagId,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request buffer address must be 16-byte aligned.
|
||||||
|
#[repr(C, align(16))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Align16<T: Sized>(PropertyMailboxRequest<T>);
|
||||||
|
|
||||||
|
/// Pack a sequence of concatenated tags into a request, and send the address
|
||||||
|
/// to the mailbox.
|
||||||
|
/// Returns `PropertyMailboxResult<typeof($tags)>`.
|
||||||
|
macro_rules! send_request {
|
||||||
|
($tags: ident) => {{
|
||||||
|
let req = Align16(PropertyMailboxRequest {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
let start = &req as *const _ as u32;
|
||||||
|
let end = start + req.0.buf_size;
|
||||||
|
{
|
||||||
|
// flush data cache around mailbox accesses
|
||||||
|
let mut mbox = MAILBOX.lock();
|
||||||
|
asm::flush_dcache_range(start as usize, end as usize);
|
||||||
|
mbox.write(MailboxChannel::Property, start);
|
||||||
|
mbox.read(MailboxChannel::Property);
|
||||||
|
asm::flush_dcache_range(start as usize, end as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
match req.0.req_resp_code {
|
||||||
|
RPI_FIRMWARE_STATUS_SUCCESS => Ok(req.0.buf),
|
||||||
|
other => Err(PropertyMailboxError(other as u32)),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a tag to mailbox. Will call `send_request!`.
|
||||||
|
/// Returns `PropertyMailboxResult<typeof(buf)>`.
|
||||||
|
macro_rules! send_one_tag {
|
||||||
|
($id: expr, [$($arg: expr),*]) => {{
|
||||||
|
let buf = [$($arg),*];
|
||||||
|
let tag = PropertyMailboxTag {
|
||||||
|
id: $id,
|
||||||
|
buf_size: mem::size_of_val(&buf) as u32,
|
||||||
|
req_resp_size: 0,
|
||||||
|
buf,
|
||||||
|
};
|
||||||
|
Ok(send_request!(tag)?.buf)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates contiguous memory on the GPU. `size` and `align` are in bytes.
|
||||||
|
/// Returns memory `handle`.
|
||||||
|
pub fn mem_alloc(size: u32, align: u32, flags: u32) -> PropertyMailboxResult<u32> {
|
||||||
|
let ret = send_one_tag!(RPI_FIRMWARE_LOCK_MEMORY, [size, align, flags])?;
|
||||||
|
Ok(ret[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free the memory buffer of `handle`. status=0 is success.
|
||||||
|
pub fn mem_free(handle: u32) -> PropertyMailboxResult<()> {
|
||||||
|
let status = send_one_tag!(RPI_FIRMWARE_RELEASE_MEMORY, [handle])?;
|
||||||
|
match status[0] {
|
||||||
|
0 => Ok(()),
|
||||||
|
other => Err(PropertyMailboxError(other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock buffer in place, and return a `bus_address`. Must be done before memory
|
||||||
|
/// can be accessed.
|
||||||
|
pub fn mem_lock(handle: u32) -> PropertyMailboxResult<u32> {
|
||||||
|
let ret = send_one_tag!(RPI_FIRMWARE_LOCK_MEMORY, [handle])?;
|
||||||
|
Ok(ret[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unlock buffer. It retains contents, but may move. Needs to be locked before
|
||||||
|
/// next use. status=0 is success.
|
||||||
|
pub fn mem_unlock(handle: u32) -> PropertyMailboxResult<()> {
|
||||||
|
let status = send_one_tag!(RPI_FIRMWARE_UNLOCK_MEMORY, [handle])?;
|
||||||
|
match status[0] {
|
||||||
|
0 => Ok(()),
|
||||||
|
other => Err(PropertyMailboxError(other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get physical (display) width/height. Returns `(width, height)` in pixels.
|
||||||
|
/// Note that the "physical (display)" size is the size of the allocated buffer
|
||||||
|
/// in memory, not the resolution of the video signal sent to the display device.
|
||||||
|
pub fn framebuffer_get_physical_size() -> PropertyMailboxResult<(u32, u32)> {
|
||||||
|
let ret = send_one_tag!(RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT, [0, 0])?;
|
||||||
|
Ok((ret[0], ret[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get depth. Returns bits per pixel.
|
||||||
|
pub fn framebuffer_get_depth() -> PropertyMailboxResult<u32> {
|
||||||
|
let ret = send_one_tag!(RPI_FIRMWARE_FRAMEBUFFER_GET_DEPTH, [0])?;
|
||||||
|
Ok(ret[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set virtual offset. Returns `(X, Y)` in pixel.
|
||||||
|
/// The response may not be the same as the request so it must be checked.
|
||||||
|
/// May be the previous offset or 0 for unsupported.
|
||||||
|
pub fn framebuffer_set_virtual_offset(xoffset: u32, yoffset: u32) -> PropertyMailboxResult<(u32, u32)> {
|
||||||
|
let ret = send_one_tag!(
|
||||||
|
RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET,
|
||||||
|
[xoffset, yoffset]
|
||||||
|
)?;
|
||||||
|
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<FramebufferInfo> {
|
||||||
|
#[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],
|
||||||
|
})
|
||||||
|
}
|
@ -1,17 +1,19 @@
|
|||||||
use bcm2837::timer;
|
use bcm2837::timer::{BasicTimer, Timer};
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
|
/// Initialization timer.
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
timer::init();
|
Timer::new().init();
|
||||||
set_next();
|
set_next();
|
||||||
info!("timer: init end");
|
info!("timer: init end");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current time in microseconds.
|
||||||
pub fn get_cycle() -> u64 {
|
pub fn get_cycle() -> u64 {
|
||||||
timer::current_time()
|
Timer::new().read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set next timer interrupt to 10 ms from now.
|
||||||
pub fn set_next() {
|
pub fn set_next() {
|
||||||
// 10 ms
|
Timer::new().tick_in(10 * 1000);
|
||||||
timer::tick_in(10 * 1000);
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
//! Frambuffer color
|
||||||
|
|
||||||
|
pub trait FramebufferColor {
|
||||||
|
/// pack as 32-bit integer
|
||||||
|
fn pack16(&self) -> u16;
|
||||||
|
|
||||||
|
/// pack as 32-bit integer
|
||||||
|
fn pack32(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct RgbColor(u8, u8, u8);
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum ConsoleColor {
|
||||||
|
Black = 0,
|
||||||
|
Red = 1,
|
||||||
|
Green = 2,
|
||||||
|
Yellow = 3,
|
||||||
|
Blue = 4,
|
||||||
|
Magenta = 5,
|
||||||
|
Cyan = 6,
|
||||||
|
White = 7,
|
||||||
|
BrightBlack = 60,
|
||||||
|
BrightRed = 61,
|
||||||
|
BrightGreen = 62,
|
||||||
|
BrightYellow = 63,
|
||||||
|
BrightBlue = 64,
|
||||||
|
BrightMagenta = 65,
|
||||||
|
BrightCyan = 66,
|
||||||
|
BrightWhite = 67,
|
||||||
|
}
|
||||||
|
use self::ConsoleColor::*;
|
||||||
|
|
||||||
|
impl From<ConsoleColor> for RgbColor {
|
||||||
|
/// Convert `ConsoleColor` to `RgbColor`.
|
||||||
|
/// use `CMD` color scheme.
|
||||||
|
/// (ref: https://en.wikipedia.org/wiki/ANSI_escape_code)
|
||||||
|
fn from(color: ConsoleColor) -> Self {
|
||||||
|
match color {
|
||||||
|
Black => RgbColor(0, 0, 0),
|
||||||
|
Red => RgbColor(128, 0, 0),
|
||||||
|
Green => RgbColor(0, 128, 8),
|
||||||
|
Yellow => RgbColor(128, 128, 0),
|
||||||
|
Blue => RgbColor(0, 0, 128),
|
||||||
|
Magenta => RgbColor(128, 0, 128),
|
||||||
|
Cyan => RgbColor(0, 128, 128),
|
||||||
|
White => RgbColor(192, 192, 192),
|
||||||
|
BrightBlack => RgbColor(128, 128, 128),
|
||||||
|
BrightRed => RgbColor(255, 0, 0),
|
||||||
|
BrightGreen => RgbColor(0, 255, 0),
|
||||||
|
BrightYellow => RgbColor(255, 255, 0),
|
||||||
|
BrightBlue => RgbColor(0, 0, 255),
|
||||||
|
BrightMagenta => RgbColor(255, 0, 255),
|
||||||
|
BrightCyan => RgbColor(0, 255, 255),
|
||||||
|
BrightWhite => RgbColor(255, 255, 255),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FramebufferColor for RgbColor {
|
||||||
|
#[inline]
|
||||||
|
fn pack16(&self) -> u16 {
|
||||||
|
// BGR565
|
||||||
|
((self.0 as u16 & 0xF8) << 8) | ((self.1 as u16 & 0xFC) << 3) | (self.2 as u16 >> 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pack32(&self) -> u32 {
|
||||||
|
// BGRA8888
|
||||||
|
// FIXME: qemu and low version RPi use RGBA order for 24/32-bit color depth,
|
||||||
|
// but RPi3 B+ uses BGRA order for 24/32-bit color depth.
|
||||||
|
((self.0 as u32) << 16) | ((self.1 as u32) << 8) | (self.2 as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FramebufferColor for ConsoleColor {
|
||||||
|
#[inline]
|
||||||
|
fn pack16(&self) -> u16 {
|
||||||
|
RgbColor::from(*self).pack16()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pack32(&self) -> u32 {
|
||||||
|
RgbColor::from(*self).pack32()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
//! Console font
|
||||||
|
|
||||||
|
mod font8x16;
|
||||||
|
|
||||||
|
pub use self::font8x16::Font8x16;
|
||||||
|
|
||||||
|
pub trait Font {
|
||||||
|
const HEIGHT: usize;
|
||||||
|
const WIDTH: usize;
|
||||||
|
|
||||||
|
/// The `y` coordinate of underline.
|
||||||
|
const UNDERLINE: usize;
|
||||||
|
/// The `y` coordinate of strikethrough.
|
||||||
|
const STRIKETHROUGH: usize;
|
||||||
|
|
||||||
|
/// Whether the character `byte` is visible at `(x, y)`.
|
||||||
|
fn get(byte: u8, x: usize, y: usize) -> bool;
|
||||||
|
}
|
@ -0,0 +1,237 @@
|
|||||||
|
//! Framebuffer console display driver for ARM64
|
||||||
|
|
||||||
|
mod color;
|
||||||
|
mod escape_parser;
|
||||||
|
mod fonts;
|
||||||
|
|
||||||
|
use self::color::FramebufferColor;
|
||||||
|
use self::escape_parser::{CharacterAttribute, EscapeParser};
|
||||||
|
use self::fonts::{Font, Font8x16};
|
||||||
|
|
||||||
|
use super::fb::{ColorDepth::*, FramebufferInfo, FRAME_BUFFER};
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::fmt;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use log::*;
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct ConsoleChar {
|
||||||
|
ascii_char: u8,
|
||||||
|
attr: CharacterAttribute,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConsoleChar {
|
||||||
|
fn default() -> Self {
|
||||||
|
ConsoleChar {
|
||||||
|
ascii_char: 0,
|
||||||
|
attr: CharacterAttribute::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Character buffer
|
||||||
|
struct ConsoleBuffer<F: Font> {
|
||||||
|
num_row: usize,
|
||||||
|
num_col: usize,
|
||||||
|
buf: Vec<Vec<ConsoleChar>>,
|
||||||
|
font: PhantomData<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Font> ConsoleBuffer<F> {
|
||||||
|
fn new(num_row: usize, num_col: usize) -> ConsoleBuffer<F> {
|
||||||
|
ConsoleBuffer {
|
||||||
|
num_row,
|
||||||
|
num_col,
|
||||||
|
buf: vec![vec![ConsoleChar::default(); num_col]; num_row],
|
||||||
|
font: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write one character at `(row, col)`.
|
||||||
|
fn write(&mut self, row: usize, col: usize, ch: ConsoleChar) {
|
||||||
|
if self.buf[row][col] == ch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.buf[row][col] = ch;
|
||||||
|
|
||||||
|
let off_x = col * F::WIDTH;
|
||||||
|
let off_y = row * F::HEIGHT;
|
||||||
|
if let Some(fb) = FRAME_BUFFER.lock().as_mut() {
|
||||||
|
let (mut foreground, mut background) = match fb.color_depth {
|
||||||
|
ColorDepth16 => (
|
||||||
|
ch.attr.foreground.pack16() as u32,
|
||||||
|
ch.attr.background.pack16() as u32,
|
||||||
|
),
|
||||||
|
ColorDepth32 => (
|
||||||
|
ch.attr.foreground.pack32(),
|
||||||
|
ch.attr.background.pack32(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
if ch.attr.reverse {
|
||||||
|
core::mem::swap(&mut foreground, &mut background);
|
||||||
|
}
|
||||||
|
let underline_y = if ch.attr.underline {
|
||||||
|
F::UNDERLINE
|
||||||
|
} else {
|
||||||
|
F::HEIGHT
|
||||||
|
};
|
||||||
|
let strikethrough_y = if ch.attr.strikethrough {
|
||||||
|
F::STRIKETHROUGH
|
||||||
|
} else {
|
||||||
|
F::HEIGHT
|
||||||
|
};
|
||||||
|
for y in 0..F::HEIGHT {
|
||||||
|
for x in 0..F::WIDTH {
|
||||||
|
let pixel = if y == underline_y || y == strikethrough_y || F::get(ch.ascii_char, x, y) {
|
||||||
|
foreground
|
||||||
|
} else {
|
||||||
|
background
|
||||||
|
};
|
||||||
|
fb.write((off_x + x) as u32, (off_y + y) as u32, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete one character at `(row, col)`.
|
||||||
|
fn delete(&mut self, row: usize, col: usize) {
|
||||||
|
self.write(row, col, ConsoleChar::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert one blank line at the bottom, and scroll up one line.
|
||||||
|
/// XXX: read framebuffer is toooo slow, do not use `fb.copy()`.
|
||||||
|
fn new_line(&mut self) {
|
||||||
|
for i in 1..self.num_row {
|
||||||
|
for j in 0..self.num_col {
|
||||||
|
self.write(i - 1, j, self.buf[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for j in 0..self.num_col {
|
||||||
|
self.write(self.num_row - 1, j, ConsoleChar::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the entire buffer and screen.
|
||||||
|
fn clear(&mut self) {
|
||||||
|
for i in 0..self.num_row {
|
||||||
|
for j in 0..self.num_col {
|
||||||
|
self.buf[i][j] = ConsoleChar::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(fb) = FRAME_BUFFER.lock().as_mut() {
|
||||||
|
fb.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Console structure
|
||||||
|
pub struct Console<F: Font> {
|
||||||
|
/// cursor row
|
||||||
|
row: usize,
|
||||||
|
/// cursor column
|
||||||
|
col: usize,
|
||||||
|
/// escape sequence parser
|
||||||
|
parser: EscapeParser,
|
||||||
|
/// character buffer
|
||||||
|
buf: ConsoleBuffer<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Font> Console<F> {
|
||||||
|
fn new(fb: &FramebufferInfo) -> Console<F> {
|
||||||
|
let num_row = fb.yres as usize / F::HEIGHT;
|
||||||
|
let num_col = fb.xres as usize / F::WIDTH;
|
||||||
|
Console {
|
||||||
|
row: 0,
|
||||||
|
col: 0,
|
||||||
|
parser: EscapeParser::new(),
|
||||||
|
buf: ConsoleBuffer::new(num_row, num_col),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_line(&mut self) {
|
||||||
|
let attr_blank = ConsoleChar {
|
||||||
|
ascii_char: 0,
|
||||||
|
attr: self.parser.char_attribute(),
|
||||||
|
};
|
||||||
|
for j in self.col..self.buf.num_col {
|
||||||
|
self.buf.write(self.row, j, attr_blank);
|
||||||
|
}
|
||||||
|
self.col = 0;
|
||||||
|
if self.row < self.buf.num_row - 1 {
|
||||||
|
self.row += 1;
|
||||||
|
} else {
|
||||||
|
self.buf.new_line();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_byte(&mut self, byte: u8) {
|
||||||
|
if self.parser.is_parsing() {
|
||||||
|
if self.parser.parse(byte) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match byte {
|
||||||
|
b'\x7f' => {
|
||||||
|
if self.col > 0 {
|
||||||
|
self.col -= 1;
|
||||||
|
self.buf.delete(self.row, self.col);
|
||||||
|
} else if self.row > 0 {
|
||||||
|
self.row -= 1;
|
||||||
|
self.col = self.buf.num_col - 1;
|
||||||
|
self.buf.delete(self.row, self.col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'\n' => self.new_line(),
|
||||||
|
b'\r' => self.col = 0,
|
||||||
|
b'\x1b' => self.parser.start_parse(),
|
||||||
|
byte => {
|
||||||
|
if self.col >= self.buf.num_col {
|
||||||
|
self.new_line();
|
||||||
|
}
|
||||||
|
|
||||||
|
let ch = ConsoleChar {
|
||||||
|
ascii_char: byte,
|
||||||
|
attr: self.parser.char_attribute(),
|
||||||
|
};
|
||||||
|
self.buf.write(self.row, self.col, ch);
|
||||||
|
self.col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.row = 0;
|
||||||
|
self.col = 0;
|
||||||
|
self.parser = EscapeParser::new();
|
||||||
|
self.buf.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Font> fmt::Write for Console<F> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for byte in s.bytes() {
|
||||||
|
self.write_byte(byte)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref CONSOLE: Mutex<Option<Console<Font8x16>>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize console driver
|
||||||
|
pub fn init() {
|
||||||
|
if let Some(fb) = FRAME_BUFFER.lock().as_ref() {
|
||||||
|
*CONSOLE.lock() = Some(Console::new(&fb.fb_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !CONSOLE.lock().is_none() {
|
||||||
|
info!("console: init end");
|
||||||
|
} else {
|
||||||
|
warn!("console: init failed");
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
/// ARM64 drivers
|
//! ARM64 drivers
|
||||||
|
|
||||||
|
use super::board;
|
||||||
use once::*;
|
use once::*;
|
||||||
|
|
||||||
use super::board;
|
pub use self::board::fb;
|
||||||
|
pub use self::board::serial;
|
||||||
|
pub mod console;
|
||||||
|
|
||||||
/// Initialize ARM64 common drivers
|
/// Initialize ARM64 common drivers
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
assert_has_not_been_called!();
|
assert_has_not_been_called!("driver::init must be called only once");
|
||||||
|
|
||||||
board::init_driver();
|
board::init_driver();
|
||||||
|
console::init();
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
//! Serial driver for aarch64.
|
//! Input/output for aarch64.
|
||||||
|
|
||||||
|
use super::driver::serial::*;
|
||||||
|
use super::driver::console::CONSOLE;
|
||||||
use core::fmt::{Arguments, Write};
|
use core::fmt::{Arguments, Write};
|
||||||
use super::board::serial::*;
|
|
||||||
|
|
||||||
pub fn getchar() -> char {
|
pub fn getchar() -> char {
|
||||||
unsafe { SERIAL_PORT.force_unlock(); }
|
unsafe { SERIAL_PORT.force_unlock() }
|
||||||
SERIAL_PORT.lock().receive() as char
|
SERIAL_PORT.lock().receive() as char
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn putfmt(fmt: Arguments) {
|
pub fn putfmt(fmt: Arguments) {
|
||||||
unsafe { SERIAL_PORT.force_unlock(); }
|
unsafe { SERIAL_PORT.force_unlock() }
|
||||||
SERIAL_PORT.lock().write_fmt(fmt).unwrap()
|
SERIAL_PORT.lock().write_fmt(fmt).unwrap();
|
||||||
|
|
||||||
|
unsafe { CONSOLE.force_unlock() }
|
||||||
|
if let Some(console) = CONSOLE.lock().as_mut() {
|
||||||
|
console.write_fmt(fmt).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate ucore_ulib;
|
||||||
|
|
||||||
|
macro_rules! color_text {
|
||||||
|
($text:expr, $color:expr) => {{
|
||||||
|
format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: Must define main() like this
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn main() {
|
||||||
|
println!(
|
||||||
|
"{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}",
|
||||||
|
color_text!("H", 31),
|
||||||
|
color_text!("e", 32),
|
||||||
|
color_text!("l", 33),
|
||||||
|
color_text!("l", 34),
|
||||||
|
color_text!("o", 35),
|
||||||
|
color_text!("R", 36),
|
||||||
|
color_text!("u", 37),
|
||||||
|
color_text!("s", 90),
|
||||||
|
color_text!("t", 91),
|
||||||
|
color_text!("u", 92),
|
||||||
|
color_text!("C", 93),
|
||||||
|
color_text!("o", 94),
|
||||||
|
color_text!("r", 95),
|
||||||
|
color_text!("e", 96),
|
||||||
|
color_text!("!", 97),
|
||||||
|
);
|
||||||
|
|
||||||
|
let text = "reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m";
|
||||||
|
println!("\x1b[47m{}\x1b[0m", color_text!(text, 30));
|
||||||
|
for i in 31..38 {
|
||||||
|
println!("{}", color_text!(text, i));
|
||||||
|
}
|
||||||
|
for i in 90..98 {
|
||||||
|
println!("{}", color_text!(text, i));
|
||||||
|
}
|
||||||
|
}
|