Merge branch 'raspi-fb' into arch-aarch64

master
equation314 6 years ago
commit f668d396ef

@ -5,6 +5,7 @@ extern crate volatile;
pub mod gpio;
pub mod timer;
pub mod mailbox;
pub mod mini_uart;
pub mod interrupt;

@ -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));
}
}

@ -2,12 +2,12 @@ use crate::IO_BASE;
use crate::gpio::{Function, Gpio};
use volatile::{ReadOnly, Volatile};
/// The base address for the `MU` registers.
const MU_REG_BASE: usize = IO_BASE + 0x215040;
/// The `AUXENB` register from page 9 of the BCM2837 documentation.
const AUX_ENABLES: *mut Volatile<u8> = (IO_BASE + 0x215004) as *mut Volatile<u8>;
/// The base address for the `MU` registers.
const MU_REG_BASE: usize = IO_BASE + 0x215040;
/// Enum representing bit fields of the `AUX_MU_IIR_REG` register.
#[repr(u8)]
pub enum MiniUartInterruptId {
@ -55,19 +55,10 @@ pub struct MiniUart {
}
impl MiniUart {
/// Initializes the mini UART by enabling it as an auxiliary peripheral,
/// setting the data size to 8 bits, setting the BAUD rate to ~115200 (baud
/// divider of 270), setting GPIO pins 14 and 15 to alternative function 5
/// (TXD1/RDXD1), and finally enabling the UART transmitter and receiver.
///
/// By default, reads will never time out. To set a read timeout, use
/// `set_read_timeout()`.
/// Returns a new instance of `MiniUart`.
#[inline]
pub fn new() -> MiniUart {
let registers = unsafe {
// Enable the mini UART as an auxiliary device.
(*AUX_ENABLES).write(1);
&mut *(MU_REG_BASE as *mut Registers)
};
let registers = unsafe { &mut *(MU_REG_BASE as *mut Registers) };
MiniUart {
registers: registers,
@ -75,7 +66,17 @@ impl MiniUart {
}
}
/// Initializes the mini UART by enabling it as an auxiliary peripheral,
/// setting the data size to 8 bits, setting the BAUD rate to ~115200 (baud
/// divider of 270), setting GPIO pins 14 and 15 to alternative function 5
/// (TXD1/RDXD1), and finally enabling the UART transmitter and receiver.
///
/// By default, reads will never time out. To set a read timeout, use
/// `set_read_timeout()`.
pub fn init(&mut self) {
// Enable the mini UART as an auxiliary device.
unsafe { (*AUX_ENABLES).write(1) }
Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0);
Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0);

@ -222,8 +222,8 @@ pub struct MemoryAttr {
user: bool,
readonly: bool,
execute: bool,
mmio: bool,
hide: bool,
mmio: u8,
}
impl MemoryAttr {
@ -251,8 +251,12 @@ impl MemoryAttr {
self.execute = true;
self
}
pub fn mmio(mut self) -> Self {
self.mmio = true;
/*
** @brief set the MMIO type
** @retval MemoryAttr the memory attribute itself
*/
pub fn mmio(mut self, value: u8) -> Self {
self.mmio = value;
self
}
/*
@ -273,9 +277,9 @@ impl MemoryAttr {
if self.user { entry.set_user(true); }
if self.readonly { entry.set_writable(false); }
if self.execute { entry.set_execute(true); }
if self.mmio { entry.set_mmio(true); }
if self.mmio != 0 { entry.set_mmio(self.mmio); }
if self.hide { entry.set_present(false); }
if self.user || self.readonly || self.execute || self.mmio || self.hide { entry.update(); }
if self.user || self.readonly || self.execute || self.mmio != 0 || self.hide { entry.update(); }
}
}

@ -195,6 +195,16 @@ pub trait Entry {
** @retval none
*/
fn set_execute(&mut self, value: bool);
fn mmio(&self) -> bool;
fn set_mmio(&mut self, value: bool);
/*
** @brief get MMIO type
** (e.g. aarch64 can have normal/device/normal_non_cacheable memory)
** @retval u8 the MMIO type
*/
fn mmio(&self) -> u8;
/*
** @brief set MMIO type
** @param value: u8 the MMIO type
** @retval none
*/
fn set_mmio(&mut self, value: u8);
}

2
kernel/Cargo.lock generated

@ -1,7 +1,7 @@
[[package]]
name = "aarch64"
version = "2.2.2"
source = "git+https://github.com/equation314/aarch64#e3b60adb233ad34d05443e0b5ec34cac29253296"
source = "git+https://github.com/equation314/aarch64#b6a0f4a3be6f74927c88305a6af5ad2be079bccd"
dependencies = [
"bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",

@ -12,6 +12,7 @@ no_mmu = []
# Kernel in M-mode (for riscv32)
m_mode = ["no_mmu"]
# (for aarch64 RaspberryPi3)
nographic = []
board_raspi3 = ["bcm2837"]
raspi3_use_generic_timer = ["bcm2837/use_generic_timer"]

@ -1,22 +1,23 @@
# Commands:
# make build Build
# make run Build and run in QEMU
# make justrun Run the last build
# make doc Generate docs
# make asm Open the deassemble file of the last build
# make header Open 'objdump -h' of the last build
# make clean Clean
# make build Build
# make run Build and run in QEMU
# make justrun Run the last build
# make doc Generate docs
# make asm Open the deassemble file of the last build
# make header Open 'objdump -h' of the last build
# make clean Clean
#
# Options:
# arch = x86_64 | riscv32 | aarch64
# d = int | in_asm | ... QEMU debug info
# mode = debug | release
# LOG = off | error | warn | info | debug | trace
# SFSIMG = SFS image path of user programs
# smp = 1 | 2 | ... SMP core number
# board = fpga Only available on riscv32, build without bbl, run on board
# | raspi3 Only available on aarch64, run on Raspberry Pi 3 Model B/B+
# m_mode Only available on riscv32, build for M-Mode, without MMU
# arch = x86_64 | riscv32 | aarch64
# d = int | in_asm | ... QEMU debug info
# mode = debug | release
# LOG = off | error | warn | info | debug | trace
# SFSIMG = <sfsimg> SFS image path of user programs
# smp = 1 | 2 | ... SMP core number
# graphic = on | off enable/disable qemu graphical output
# board = fpga Only available on riscv32, build without bbl, run on board
# | raspi3 Only available on aarch64, run on Raspberry Pi 3 Model B/B+
# m_mode Only available on riscv32, build for M-Mode, without MMU
arch ?= riscv32
board ?= raspi3
@ -24,7 +25,7 @@ mode ?= debug
LOG ?= debug
smp ?= 4
# NOTE: crate 'process' use this name 'm_mode' as an environment
# to set interrupt (MIE or SIE)
# to set interrupt (MIE or SIE)
m_mode ?=
target := $(arch)-blog_os
@ -40,10 +41,15 @@ user_obj := build/$(arch)/user.o
export ARCH = $(arch)
export SFSIMG = $(user_dir)/build/user-$(arch).img
ifeq ($(arch), aarch64)
graphic ?= on
else
graphic ?= off
endif
### qemu options ###
qemu_opts := \
-smp cores=$(smp) \
-nographic
-smp cores=$(smp)
ifeq ($(arch), x86_64)
qemu_opts += \
@ -67,6 +73,10 @@ qemu_opts += \
-kernel $(bin)
endif
ifneq ($(graphic), on)
qemu_opts += -nographic
endif
ifdef d
qemu_opts += -d $(d)
endif
@ -78,6 +88,10 @@ features += no_bbl
endif
endif
ifneq ($(graphic), on)
features += nographic
endif
ifeq ($(board), raspi3)
# qemu only has generic timer
# TODO: configure system/generic timer automatically

@ -0,0 +1,231 @@
//! 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;
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),
}
}

@ -1,6 +1,5 @@
use crate::arch::interrupt::TrapFrame;
use bcm2837::interrupt::Controller;
use log::*;
pub use bcm2837::interrupt::Interrupt;

@ -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],
})
}

@ -2,23 +2,27 @@
use once::*;
pub mod fb;
pub mod irq;
pub mod timer;
pub mod serial;
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() {
#[cfg(not(feature = "nographic"))]
fb::init();
timer::init();
}

@ -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 {
@ -21,7 +22,9 @@ impl SerialPort {
}
/// Init a newly created SerialPort, can only be called once.
pub fn init(&mut self) {
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<SerialPort> = Mutex::new(SerialPort::new());
}
pub fn init() {
SERIAL_PORT.lock().init();
}

@ -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,152 @@
//! ANSI escape sequences parser
//! (ref: https://en.wikipedia.org/wiki/ANSI_escape_code)
use super::color::{ConsoleColor, ConsoleColor::*, FramebufferColor};
use alloc::vec::Vec;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CharacterAttribute<C: FramebufferColor = ConsoleColor> {
/// foreground color
pub foreground: C,
/// background color
pub background: C,
/// show underline
pub underline: bool,
/// swap foreground and background colors
pub reverse: bool,
/// text marked cfor deletion
pub strikethrough: bool,
}
impl Default for CharacterAttribute {
fn default() -> Self {
CharacterAttribute {
foreground: White,
background: Black,
underline: false,
reverse: false,
strikethrough: false,
}
}
}
#[derive(Debug, PartialEq)]
enum ParseStatus {
/// The last character is `ESC`, start parsing the escape sequence.
BeginEscapeSequence,
/// The character followed by `ESC` is `[`, start parsing the CSI (Control
/// Sequence Introducer) sequence. The CSI sequence format is like
/// `ESC [ n1 ; n2 ; ... m`.
ParsingCSI,
/// Display text Normally.
Text,
}
#[derive(Debug)]
pub struct EscapeParser {
status: ParseStatus,
char_attr: CharacterAttribute,
current_param: Option<u8>,
params: Vec<u8>,
}
impl EscapeParser {
pub fn new() -> EscapeParser {
EscapeParser {
status: ParseStatus::Text,
char_attr: CharacterAttribute::default(),
params: Vec::new(),
current_param: None,
}
}
pub fn is_parsing(&self) -> bool {
self.status != ParseStatus::Text
}
/// See an `ECS` character, start parsing escape sequence.
pub fn start_parse(&mut self) {
assert!(self.status == ParseStatus::Text);
self.status = ParseStatus::BeginEscapeSequence;
self.current_param = None;
}
//// Parse SGR (Select Graphic Rendition) parameters.
fn parse_sgr_params(&mut self) {
use core::mem::transmute;
for param in &self.params {
match param {
0 => self.char_attr = CharacterAttribute::default(),
4 => self.char_attr.underline = true,
7 => self.char_attr.reverse = true,
9 => self.char_attr.strikethrough = true,
24 => self.char_attr.underline = false,
27 => self.char_attr.reverse = false,
29 => self.char_attr.strikethrough = false,
30...37 | 90...97 => self.char_attr.foreground = unsafe { transmute(param - 30) },
40...47 | 100...107 => self.char_attr.background = unsafe { transmute(param - 40) },
_ => { /* unimplemented!() */ }
}
}
}
/// See a character during parsing.
pub fn parse(&mut self, byte: u8) -> bool {
assert!(self.status != ParseStatus::Text);
match self.status {
ParseStatus::BeginEscapeSequence => match byte {
b'[' => {
self.status = ParseStatus::ParsingCSI;
self.current_param = Some(0);
self.params.clear();
return true;
}
_ => { /* unimplemented!() */ }
},
ParseStatus::ParsingCSI => match byte {
b'0'...b'9' => {
let digit = (byte - b'0') as u32;
if let Some(param) = self.current_param {
let res: u32 = param as u32 * 10 + digit;
self.current_param = if res <= 0xFF { Some(res as u8) } else { None };
}
return true;
}
b';' => {
if let Some(param) = self.current_param {
self.params.push(param);
}
self.current_param = Some(0);
return true;
}
// @AZ[\]^_`az{|}~
0x40...0x7E => {
if let Some(param) = self.current_param {
self.params.push(param);
}
match byte {
b'm' => self.parse_sgr_params(),
_ => { /* unimplemented!() */ }
}
self.status = ParseStatus::Text;
self.current_param = None;
self.params.clear();
return true;
}
_ => {}
},
ParseStatus::Text => {}
}
self.status = ParseStatus::Text;
self.current_param = None;
self.params.clear();
false
}
pub fn char_attribute(&self) -> CharacterAttribute {
self.char_attr
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,16 @@
//! Console font
mod font8x16;
pub use self::font8x16::Font8x16;
pub trait Font {
const HEIGHT: usize;
const WIDTH: usize;
const UNDERLINE: usize;
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 super::board;
pub use self::board::fb;
pub use self::board::serial;
pub mod console;
/// Initialize ARM64 common drivers
pub fn init() {
assert_has_not_been_called!();
assert_has_not_been_called!("driver::init must be called only once");
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 super::board::serial::*;
pub fn getchar() -> char {
unsafe { SERIAL_PORT.force_unlock(); }
unsafe { SERIAL_PORT.force_unlock() }
SERIAL_PORT.lock().receive() as char
}
pub fn putfmt(fmt: Arguments) {
unsafe { SERIAL_PORT.force_unlock(); }
SERIAL_PORT.lock().write_fmt(fmt).unwrap()
unsafe { SERIAL_PORT.force_unlock() }
SERIAL_PORT.lock().write_fmt(fmt).unwrap();
unsafe { CONSOLE.force_unlock() }
if let Some(console) = CONSOLE.lock().as_mut() {
console.write_fmt(fmt).unwrap();
}
}

@ -1,11 +1,14 @@
//! Memory initialization for aarch64.
use crate::memory::{init_heap, MemoryArea, MemoryAttr, MemorySet, FRAME_ALLOCATOR};
use super::paging::MMIOType;
use aarch64::paging::{memory_attribute::*, PhysFrame as Frame};
use aarch64::{addr::*, barrier, regs::*};
use atags::atags::Atags;
use lazy_static::lazy_static;
use log::*;
use spin::Mutex;
use ucore_memory::PAGE_SIZE;
use atags::atags::Atags;
use aarch64::{barrier, regs::*, addr::*};
use aarch64::paging::{PhysFrame as Frame, memory_attribute::*};
use crate::memory::{FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet};
/// Memory initialization.
pub fn init() {
@ -30,8 +33,8 @@ pub fn init_mmu_early() {
// device.
MAIR_EL1.write(
MAIR_EL1::Attr0.val(MairDevice::config_value()) +
MAIR_EL1::Attr1.val(MairNormal::config_value()) +
MAIR_EL1::Attr0.val(MairNormal::config_value()) +
MAIR_EL1::Attr1.val(MairDevice::config_value()) +
MAIR_EL1::Attr2.val(MairNormalNonCacheable::config_value()),
);
@ -62,19 +65,19 @@ pub fn init_mmu_early() {
// Switch the MMU on.
//
// First, force all previous changes to be seen before the MMU is enabled.
unsafe { barrier::isb(barrier::SY); }
unsafe { barrier::isb(barrier::SY) }
// Enable the MMU and turn on data and instruction caching.
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable);
// Force MMU init to complete before next instruction
unsafe { barrier::isb(barrier::SY); }
unsafe { barrier::isb(barrier::SY) }
}
fn init_frame_allocator() {
use crate::consts::MEMORY_OFFSET;
use bit_allocator::BitAlloc;
use core::ops::Range;
use crate::consts::MEMORY_OFFSET;
let (start, end) = memory_map().expect("failed to find memory map");
let mut ba = FRAME_ALLOCATOR.lock();
@ -82,14 +85,14 @@ fn init_frame_allocator() {
info!("FrameAllocator init end");
/*
* @param:
* start: start address
* end: end address
* @brief:
* transform the memory address to the page number
* @retval:
* the page number range from start address to end address
*/
* @param:
* start: start address
* end: end address
* @brief:
* transform the memory address to the page number
* @retval:
* the page number range from start address to end address
*/
fn to_range(start: usize, end: usize) -> Range<usize> {
let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE;
let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1;
@ -97,9 +100,13 @@ fn init_frame_allocator() {
}
}
lazy_static! {
pub static ref KERNEL_MEMORY_SET: Mutex<MemorySet> = Mutex::new(MemorySet::new_bare());
}
/// remap kernel page table after all initialization.
fn remap_the_kernel() {
let mut ms = MemorySet::new_bare();
let mut ms = KERNEL_MEMORY_SET.lock();
ms.push(MemoryArea::new_identity(0, bootstacktop as usize, MemoryAttr::default(), "kstack"));
ms.push(MemoryArea::new_identity(stext as usize, etext as usize, MemoryAttr::default().execute().readonly(), "text"));
ms.push(MemoryArea::new_identity(sdata as usize, edata as usize, MemoryAttr::default(), "data"));
@ -107,13 +114,29 @@ fn remap_the_kernel() {
ms.push(MemoryArea::new_identity(sbss as usize, ebss as usize, MemoryAttr::default(), "bss"));
use super::board::{IO_REMAP_BASE, IO_REMAP_END};
ms.push(MemoryArea::new_identity(IO_REMAP_BASE, IO_REMAP_END, MemoryAttr::default().mmio(), "io_remap"));
unsafe { ms.get_page_table_mut().activate_as_kernel(); }
::core::mem::forget(ms);
ms.push(MemoryArea::new_identity(
IO_REMAP_BASE,
IO_REMAP_END,
MemoryAttr::default().mmio(MMIOType::Device as u8),
"io_remap",
));
unsafe { ms.get_page_table_mut().activate_as_kernel() }
info!("kernel remap end");
}
pub fn ioremap(start: usize, len: usize, name: &'static str) -> usize {
let mut ms = KERNEL_MEMORY_SET.lock();
let area = MemoryArea::new_identity(
start,
start + len,
MemoryAttr::default().mmio(MMIOType::NormalNonCacheable as u8),
name,
);
ms.push(area);
start
}
/// Returns the (start address, end address) of the available memory on this
/// system if it can be determined. If it cannot, `None` is returned.
///
@ -131,7 +154,7 @@ fn memory_map() -> Option<(usize, usize)> {
None
}
extern {
extern "C" {
fn bootstacktop();
fn stext();
fn etext();

@ -18,13 +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_early();
println!("{}", LOGO);
board::init_serial_early();
crate::logging::init();
interrupt::init();
memory::init();
driver::init();
println!("{}", LOGO);
crate::process::init();

@ -22,7 +22,7 @@ pub fn setup_temp_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: F
p2.zero();
let (start_addr, end_addr) = (0, 0x40000000);
let block_flags = EF::VALID | EF::AF | EF::WRITE | EF::XN;
let block_flags = EF::VALID | EF::AF | EF::WRITE | EF::UXN;
for page in Page::<Size2MiB>::range_of(start_addr, end_addr) {
let paddr = PhysAddr::new(page.start_address().as_u64());
@ -106,6 +106,14 @@ impl ActivePageTable {
}
}
#[repr(u8)]
pub enum MMIOType {
Normal = 0,
Device = 1,
NormalNonCacheable = 2,
Unsupported = 3,
}
impl Entry for PageEntry {
fn update(&mut self) {
let addr = VirtAddr::new_unchecked((self as *const _ as u64) << 9);
@ -150,28 +158,39 @@ impl Entry for PageEntry {
}
fn execute(&self) -> bool {
if self.user() {
!self.0.flags().contains(EF::XN)
!self.0.flags().contains(EF::UXN)
} else {
!self.0.flags().contains(EF::PXN)
}
}
fn set_execute(&mut self, value: bool) {
if self.user() {
self.as_flags().set(EF::XN, !value)
self.as_flags().set(EF::UXN, !value)
} else {
self.as_flags().set(EF::PXN, !value)
}
}
fn mmio(&self) -> bool {
self.0.attr().value == MairDevice::attr_value().value
}
fn set_mmio(&mut self, value: bool) {
if value {
self.0.modify_attr(MairDevice::attr_value())
fn mmio(&self) -> u8 {
let value = self.0.attr().value;
if value == MairNormal::attr_value().value {
0
} else if value == MairDevice::attr_value().value {
1
} else if value == MairNormalNonCacheable::attr_value().value {
2
} else {
self.0.modify_attr(MairNormal::attr_value())
3
}
}
fn set_mmio(&mut self, value: u8) {
let attr = match value {
0 => MairNormal::attr_value(),
1 => MairDevice::attr_value(),
2 => MairNormalNonCacheable::attr_value(),
_ => return,
};
self.0.modify_attr(attr);
}
}
impl PageEntry {

@ -26,7 +26,7 @@ pub fn setup_page_table(frame: Frame) {
// Set kernel identity map
// 0x10000000 ~ 1K area
p2.map_identity(0x40, EF::VALID | EF::READABLE | EF::WRITABLE);
// 0x80000000 ~ 12M area
// 0x80000000 ~ 12M area
p2.map_identity(KERNEL_P2_INDEX, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE);
p2.map_identity(KERNEL_P2_INDEX + 1, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE);
p2.map_identity(KERNEL_P2_INDEX + 2, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE);
@ -196,8 +196,8 @@ impl Entry for PageEntry {
fn set_user(&mut self, value: bool) { self.as_flags().set(EF::USER, value); }
fn execute(&self) -> bool { self.0.flags().contains(EF::EXECUTABLE) }
fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::EXECUTABLE, value); }
fn mmio(&self) -> bool { unimplemented!() }
fn set_mmio(&mut self, value: bool) { unimplemented!() }
fn mmio(&self) -> u8 { 0 }
fn set_mmio(&mut self, _value: u8) { }
}
impl PageEntry {

@ -142,8 +142,8 @@ impl Entry for PageEntry {
}
fn execute(&self) -> bool { !self.0.flags().contains(EF::NO_EXECUTE) }
fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::NO_EXECUTE, !value); }
fn mmio(&self) -> bool { unimplemented!() }
fn set_mmio(&mut self, value: bool) { unimplemented!() }
fn mmio(&self) -> u8 { 0 }
fn set_mmio(&mut self, _value: u8) { }
}
fn get_entry_ptr(addr: usize, level: u8) -> *mut PageEntry {

@ -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};

@ -25,8 +25,8 @@
#hdmi_force_hotplug=1
# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1
hdmi_group=2 # DMT
hdmi_mode=85 # 1280x720
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes

@ -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));
}
}
Loading…
Cancel
Save