# Conflicts: # .gitignore # kernel/Cargo.lock # kernel/Cargo.toml # kernel/Makefile # kernel/riscv32-blog_os.json # kernel/src/consts.rs # kernel/src/fs.rs # kernel/src/lib.rs # kernel/src/process/context.rs # kernel/src/process/mod.rsmaster
commit
2daf8c188d
@ -0,0 +1,14 @@
|
|||||||
|
[[package]]
|
||||||
|
name = "atags"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "volatile"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ca391c55768e479d5c2f8beb40c136df09257292a809ea514e82cfdfc15d00"
|
@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "atags"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["koumingyang <1761674434@qq.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
@ -0,0 +1,67 @@
|
|||||||
|
use raw;
|
||||||
|
use core::slice;
|
||||||
|
use core::str;
|
||||||
|
|
||||||
|
pub use raw::{Core, Mem};
|
||||||
|
|
||||||
|
/// An ATAG.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Atag {
|
||||||
|
Core(raw::Core),
|
||||||
|
Mem(raw::Mem),
|
||||||
|
Cmd(&'static str),
|
||||||
|
Unknown(u32),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atag {
|
||||||
|
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
|
||||||
|
pub fn core(self) -> Option<Core> {
|
||||||
|
match self {
|
||||||
|
Atag::Core(x) => Some(x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some` if this is a `Mem` ATAG. Otherwise returns `None`.
|
||||||
|
pub fn mem(self) -> Option<Mem> {
|
||||||
|
match self {
|
||||||
|
Atag::Mem(x) => Some(x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some` with the command line string if this is a `Cmd` ATAG.
|
||||||
|
/// Otherwise returns `None`.
|
||||||
|
pub fn cmd(self) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
Atag::Cmd(x) => Some(x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert between raw::* types and Atag wrapper.
|
||||||
|
impl<'a> From<&'a raw::Atag> for Atag {
|
||||||
|
fn from(atag: &raw::Atag) -> Atag {
|
||||||
|
unsafe {
|
||||||
|
match (atag.tag, &atag.kind) {
|
||||||
|
(raw::Atag::CORE, &raw::Kind { core }) => Atag::Core(core),
|
||||||
|
(raw::Atag::MEM, &raw::Kind { mem }) => Atag::Mem(mem),
|
||||||
|
(raw::Atag::CMDLINE, &raw::Kind { ref cmd }) => {
|
||||||
|
let mut cmd_ptr: *const u8 = &cmd.cmd as *const u8;
|
||||||
|
let mut len: usize = 0;
|
||||||
|
|
||||||
|
while *cmd_ptr.add(len) != 0 {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cmd_slice = slice::from_raw_parts(cmd_ptr, len);
|
||||||
|
Atag::Cmd(str::from_utf8_unchecked(cmd_slice))
|
||||||
|
},
|
||||||
|
(raw::Atag::NONE, _) => Atag::None,
|
||||||
|
(id, _) => Atag::Unknown(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
pub use atag::*;
|
||||||
|
use raw;
|
||||||
|
|
||||||
|
/// The address at which the firmware loads the ATAGS.
|
||||||
|
const ATAG_BASE: usize = 0x100;
|
||||||
|
|
||||||
|
/// An iterator over the ATAGS on this system.
|
||||||
|
pub struct Atags {
|
||||||
|
ptr: &'static raw::Atag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atags {
|
||||||
|
/// Returns an instance of `Atags`, an iterator over ATAGS on this system.
|
||||||
|
pub fn get() -> Atags {
|
||||||
|
Atags {
|
||||||
|
ptr: unsafe { &*(ATAG_BASE as *const raw::Atag) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Atags {
|
||||||
|
type Item = Atag;
|
||||||
|
|
||||||
|
/// Iterate over Atags. Returns a valid Atag until the iterator hits the
|
||||||
|
/// Atag::None.
|
||||||
|
fn next(&mut self) -> Option<Atag> {
|
||||||
|
let cur = self.ptr;
|
||||||
|
match cur.next() {
|
||||||
|
Some(next) => {
|
||||||
|
let result = Some(Atag::from(cur));
|
||||||
|
self.ptr = next;
|
||||||
|
result
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
mod raw;
|
||||||
|
mod atag;
|
||||||
|
|
||||||
|
pub mod atags;
|
@ -0,0 +1,67 @@
|
|||||||
|
/// A raw `ATAG` as laid out in memory.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Atag {
|
||||||
|
pub dwords: u32,
|
||||||
|
pub tag: u32,
|
||||||
|
pub kind: Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atag {
|
||||||
|
pub const NONE: u32 = 0x00000000;
|
||||||
|
pub const CORE: u32 = 0x54410001;
|
||||||
|
pub const MEM: u32 = 0x54410002;
|
||||||
|
pub const VIDEOTEXT: u32 = 0x54410003;
|
||||||
|
pub const RAMDISK: u32 = 0x54410004;
|
||||||
|
pub const INITRD2: u32 = 0x54420005;
|
||||||
|
pub const SERIAL: u32 = 0x54410006;
|
||||||
|
pub const REVISION: u32 = 0x54410007;
|
||||||
|
pub const VIDEOLFB: u32 = 0x54410008;
|
||||||
|
pub const CMDLINE: u32 = 0x54410009;
|
||||||
|
|
||||||
|
/// Returns the ATAG following `self`, if there is one.
|
||||||
|
pub fn next(&self) -> Option<&Atag> {
|
||||||
|
if self.tag == Atag::NONE {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let current = self as *const Atag as *const u32;
|
||||||
|
let next: &Atag = unsafe {
|
||||||
|
&*(current.add(self.dwords as usize) as *const Atag)
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The possible variant of an ATAG.
|
||||||
|
#[repr(C)]
|
||||||
|
pub union Kind {
|
||||||
|
pub core: Core,
|
||||||
|
pub mem: Mem,
|
||||||
|
pub cmd: Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `CORE` ATAG.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Core {
|
||||||
|
pub flags: u32,
|
||||||
|
pub page_size: u32,
|
||||||
|
pub root_dev: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `MEM` ATAG.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Mem {
|
||||||
|
pub size: u32,
|
||||||
|
pub start: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `CMDLINE` ATAG.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Cmd {
|
||||||
|
/// The first byte of the command line string.
|
||||||
|
pub cmd: u8
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "bcm2837"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["equation314 <equation618@gmail.com>"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
use_generic_timer = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
volatile = "0.2.4"
|
||||||
|
cortex-a = "2.2.2"
|
@ -0,0 +1,163 @@
|
|||||||
|
use IO_BASE;
|
||||||
|
use timer::delay;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use volatile::{ReadOnly, Volatile, WriteOnly};
|
||||||
|
|
||||||
|
/// The base address of the `GPIO` registers.
|
||||||
|
const GPIO_BASE: usize = IO_BASE + 0x200000;
|
||||||
|
|
||||||
|
/// An alternative GPIO function. (ref: peripherals 6.1, page 92)
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum Function {
|
||||||
|
Input = 0b000,
|
||||||
|
Output = 0b001,
|
||||||
|
Alt0 = 0b100,
|
||||||
|
Alt1 = 0b101,
|
||||||
|
Alt2 = 0b110,
|
||||||
|
Alt3 = 0b111,
|
||||||
|
Alt4 = 0b011,
|
||||||
|
Alt5 = 0b010,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GPIO registers starting from `GPIO_BASE` (ref: peripherals 6.1, page 90)
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct Registers {
|
||||||
|
FSEL: [Volatile<u32>; 6],
|
||||||
|
__reserved0: u32,
|
||||||
|
SET: [WriteOnly<u32>; 2],
|
||||||
|
__reserved1: u32,
|
||||||
|
CLR: [WriteOnly<u32>; 2],
|
||||||
|
__reserved2: u32,
|
||||||
|
LEV: [ReadOnly<u32>; 2],
|
||||||
|
__reserved3: u32,
|
||||||
|
EDS: [Volatile<u32>; 2],
|
||||||
|
__reserved4: u32,
|
||||||
|
REN: [Volatile<u32>; 2],
|
||||||
|
__reserved5: u32,
|
||||||
|
FEN: [Volatile<u32>; 2],
|
||||||
|
__reserved6: u32,
|
||||||
|
HEN: [Volatile<u32>; 2],
|
||||||
|
__reserved7: u32,
|
||||||
|
LEN: [Volatile<u32>; 2],
|
||||||
|
__reserved8: u32,
|
||||||
|
AREN: [Volatile<u32>; 2],
|
||||||
|
__reserved9: u32,
|
||||||
|
AFEN: [Volatile<u32>; 2],
|
||||||
|
__reserved10: u32,
|
||||||
|
PUD: Volatile<u32>,
|
||||||
|
PUDCLK: [Volatile<u32>; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible states for a GPIO pin.
|
||||||
|
pub enum Uninitialized {}
|
||||||
|
pub enum Input {}
|
||||||
|
pub enum Output {}
|
||||||
|
pub enum Alt {}
|
||||||
|
|
||||||
|
/// A GPIO pin in state `State`.
|
||||||
|
///
|
||||||
|
/// The `State` generic always corresponds to an uninstantiatable type that is
|
||||||
|
/// use solely to mark and track the state of a given GPIO pin. A `Gpio`
|
||||||
|
/// structure starts in the `Uninitialized` state and must be transitions into
|
||||||
|
/// one of `Input`, `Output`, or `Alt` via the `into_input`, `into_output`, and
|
||||||
|
/// `into_alt` methods before it can be used.
|
||||||
|
pub struct Gpio<State> {
|
||||||
|
pin: u8,
|
||||||
|
registers: &'static mut Registers,
|
||||||
|
_state: PhantomData<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Gpio<T> {
|
||||||
|
/// Transitions `self` to state `S`, consuming `self` and returning a new
|
||||||
|
/// `Gpio` instance in state `S`. This method should _never_ be exposed to
|
||||||
|
/// the public!
|
||||||
|
#[inline(always)]
|
||||||
|
fn transition<S>(self) -> Gpio<S> {
|
||||||
|
Gpio {
|
||||||
|
pin: self.pin,
|
||||||
|
registers: self.registers,
|
||||||
|
_state: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the Gpio pull-up/pull-down state for values in `pin_value`
|
||||||
|
/// (ref: peripherals 6.1, page 101)
|
||||||
|
pub fn set_gpio_pd(&mut self, pud_value: u8) {
|
||||||
|
let index = if self.pin >= 32 { 1 } else { 0 };
|
||||||
|
|
||||||
|
self.registers.PUD.write(pud_value as u32);
|
||||||
|
delay(150);
|
||||||
|
self.registers.PUDCLK[index as usize].write((1 << self.pin) as u32);
|
||||||
|
delay(150);
|
||||||
|
self.registers.PUD.write(0);
|
||||||
|
self.registers.PUDCLK[index as usize].write(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpio<Uninitialized> {
|
||||||
|
/// Returns a new `GPIO` structure for pin number `pin`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `pin` > `53`.
|
||||||
|
pub fn new(pin: u8) -> Gpio<Uninitialized> {
|
||||||
|
if pin > 53 {
|
||||||
|
panic!("Gpio::new(): pin {} exceeds maximum of 53", pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
Gpio {
|
||||||
|
registers: unsafe { &mut *(GPIO_BASE as *mut Registers) },
|
||||||
|
pin: pin,
|
||||||
|
_state: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables the alternative function `function` for `self`. Consumes self
|
||||||
|
/// and returns a `Gpio` structure in the `Alt` state.
|
||||||
|
pub fn into_alt(self, function: Function) -> Gpio<Alt> {
|
||||||
|
let select = (self.pin / 10) as usize;
|
||||||
|
let offset = 3 * (self.pin % 10) as usize;
|
||||||
|
self.registers.FSEL[select].update(|value| {
|
||||||
|
*value &= !(0b111 << offset);
|
||||||
|
*value |= (function as u32) << offset;
|
||||||
|
});
|
||||||
|
self.transition()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets this pin to be an _output_ pin. Consumes self and returns a `Gpio`
|
||||||
|
/// structure in the `Output` state.
|
||||||
|
pub fn into_output(self) -> Gpio<Output> {
|
||||||
|
self.into_alt(Function::Output).transition()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets this pin to be an _input_ pin. Consumes self and returns a `Gpio`
|
||||||
|
/// structure in the `Input` state.
|
||||||
|
pub fn into_input(self) -> Gpio<Input> {
|
||||||
|
self.into_alt(Function::Input).transition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpio<Output> {
|
||||||
|
/// Sets (turns on) the pin.
|
||||||
|
pub fn set(&mut self) {
|
||||||
|
let index = if self.pin >= 32 { 1 } else { 0 };
|
||||||
|
self.registers.SET[index as usize].write(1 << (self.pin - index * 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears (turns off) the pin.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
let index = if self.pin >= 32 { 1 } else { 0 };
|
||||||
|
self.registers.CLR[index as usize].write(1 << (self.pin - index * 32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpio<Input> {
|
||||||
|
/// Reads the pin's value. Returns `true` if the level is high and `false`
|
||||||
|
/// if the level is low.
|
||||||
|
pub fn level(&mut self) -> bool {
|
||||||
|
let index = if self.pin >= 32 { 1 } else { 0 };
|
||||||
|
let high = 1 << (self.pin - index * 32);
|
||||||
|
(self.registers.LEV[index as usize].read() & high) == high
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
use IO_BASE;
|
||||||
|
use volatile::{ReadOnly, Volatile};
|
||||||
|
|
||||||
|
const INT_BASE: usize = IO_BASE + 0xB000 + 0x200;
|
||||||
|
|
||||||
|
/// Allowed interrupts (ref: peripherals 7.5, page 113)
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub enum Interrupt {
|
||||||
|
Timer1 = 1,
|
||||||
|
Timer3 = 3,
|
||||||
|
Usb = 9,
|
||||||
|
Aux = 29,
|
||||||
|
Gpio0 = 49,
|
||||||
|
Gpio1 = 50,
|
||||||
|
Gpio2 = 51,
|
||||||
|
Gpio3 = 52,
|
||||||
|
Uart = 57,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupts registers starting from `INT_BASE` (ref: peripherals 7.5, page 112)
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct Registers {
|
||||||
|
IRQBasicPending: ReadOnly<u32>,
|
||||||
|
IRQPending: [ReadOnly<u32>; 2],
|
||||||
|
FIQControl: Volatile<u32>,
|
||||||
|
EnableIRQ: [Volatile<u32>; 2],
|
||||||
|
EnableBasicIRQ: Volatile<u32>,
|
||||||
|
DisableIRQ: [Volatile<u32>; 2],
|
||||||
|
DisableBasicIRQ: Volatile<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An interrupt controller. Used to enable and disable interrupts as well as to
|
||||||
|
/// check if an interrupt is pending.
|
||||||
|
pub struct Controller {
|
||||||
|
registers: &'static mut Registers,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Controller {
|
||||||
|
/// Returns a new handle to the interrupt controller.
|
||||||
|
pub fn new() -> Controller {
|
||||||
|
Controller {
|
||||||
|
registers: unsafe { &mut *(INT_BASE as *mut Registers) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables the interrupt `int`.
|
||||||
|
pub fn enable(&mut self, int: Interrupt) {
|
||||||
|
self.registers.EnableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables the interrupt `int`.
|
||||||
|
pub fn disable(&mut self, int: Interrupt) {
|
||||||
|
self.registers.DisableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `int` is pending. Otherwise, returns `false`.
|
||||||
|
pub fn is_pending(&self, int: Interrupt) -> bool {
|
||||||
|
self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![feature(asm)]
|
||||||
|
|
||||||
|
extern crate volatile;
|
||||||
|
|
||||||
|
pub mod gpio;
|
||||||
|
pub mod timer;
|
||||||
|
pub mod mini_uart;
|
||||||
|
pub mod interrupt;
|
||||||
|
|
||||||
|
pub const IO_BASE: usize = 0x3F000000;
|
@ -0,0 +1,118 @@
|
|||||||
|
use IO_BASE;
|
||||||
|
use 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>;
|
||||||
|
|
||||||
|
/// Enum representing bit fields of the `AUX_MU_LSR_REG` register.
|
||||||
|
#[repr(u8)]
|
||||||
|
enum LsrStatus {
|
||||||
|
DataReady = 1,
|
||||||
|
TxAvailable = 1 << 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MU registers starting from `AUX_ENABLES` (ref: peripherals 2.1, page 8)
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct Registers {
|
||||||
|
AUX_MU_IO_REG: Volatile<u8>,
|
||||||
|
__r0: [u8; 3],
|
||||||
|
AUX_MU_IER_REG: Volatile<u8>,
|
||||||
|
__r1: [u8; 3],
|
||||||
|
AUX_MU_IIR_REG: Volatile<u8>,
|
||||||
|
__r2: [u8; 3],
|
||||||
|
AUX_MU_LCR_REG: Volatile<u8>,
|
||||||
|
__r3: [u8; 3],
|
||||||
|
AUX_MU_MCR_REG: Volatile<u8>,
|
||||||
|
__r4: [u8; 3],
|
||||||
|
AUX_MU_LSR_REG: ReadOnly<u8>,
|
||||||
|
__r5: [u8; 3],
|
||||||
|
AUX_MU_MSR_REG: ReadOnly<u8>,
|
||||||
|
__r6: [u8; 3],
|
||||||
|
AUX_MU_SCRATCH: Volatile<u8>,
|
||||||
|
__r7: [u8; 3],
|
||||||
|
AUX_MU_CNTL_REG: Volatile<u8>,
|
||||||
|
__r8: [u8; 3],
|
||||||
|
AUX_MU_STAT_REG: ReadOnly<u32>,
|
||||||
|
AUX_MU_BAUD_REG: Volatile<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Raspberry Pi's "mini UART".
|
||||||
|
pub struct MiniUart {
|
||||||
|
registers: &'static mut Registers,
|
||||||
|
timeout: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 new() -> MiniUart {
|
||||||
|
let registers = unsafe {
|
||||||
|
// Enable the mini UART as an auxiliary device.
|
||||||
|
(*AUX_ENABLES).write(1);
|
||||||
|
&mut *(MU_REG_BASE as *mut Registers)
|
||||||
|
};
|
||||||
|
|
||||||
|
Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0);
|
||||||
|
Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0);
|
||||||
|
|
||||||
|
registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now)
|
||||||
|
registers.AUX_MU_IER_REG.write(0); // Disable receive and transmit interrupts
|
||||||
|
registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode
|
||||||
|
registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high
|
||||||
|
registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200
|
||||||
|
|
||||||
|
registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver
|
||||||
|
|
||||||
|
MiniUart {
|
||||||
|
registers: registers,
|
||||||
|
timeout: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the read timeout to `milliseconds` milliseconds.
|
||||||
|
pub fn set_read_timeout(&mut self, milliseconds: u32) {
|
||||||
|
self.timeout = Some(milliseconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the byte `byte`. This method blocks until there is space available
|
||||||
|
/// in the output FIFO.
|
||||||
|
pub fn write_byte(&mut self, byte: u8) {
|
||||||
|
while self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::TxAvailable as u8) == 0 {}
|
||||||
|
self.registers.AUX_MU_IO_REG.write(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if there is at least one byte ready to be read. If this
|
||||||
|
/// method returns `true`, a subsequent call to `read_byte` is guaranteed to
|
||||||
|
/// return immediately. This method does not block.
|
||||||
|
pub fn has_byte(&self) -> bool {
|
||||||
|
self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::DataReady as u8) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blocks until there is a byte ready to read. If a read timeout is set,
|
||||||
|
/// this method blocks for at most that amount of time. Otherwise, this
|
||||||
|
/// method blocks indefinitely until there is a byte to read.
|
||||||
|
///
|
||||||
|
/// Returns `Ok(())` if a byte is ready to read. Returns `Err(())` if the
|
||||||
|
/// timeout expired while waiting for a byte to be ready. If this method
|
||||||
|
/// returns `Ok(())`, a subsequent call to `read_byte` is guaranteed to
|
||||||
|
/// return immediately.
|
||||||
|
pub fn wait_for_byte(&self) -> Result<(), ()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a byte. Blocks indefinitely until a byte is ready to be read.
|
||||||
|
pub fn read_byte(&mut self) -> u8 {
|
||||||
|
while !self.has_byte() {}
|
||||||
|
self.registers.AUX_MU_IO_REG.read()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
extern crate cortex_a;
|
||||||
|
|
||||||
|
use self::cortex_a::regs::*;
|
||||||
|
use volatile::*;
|
||||||
|
|
||||||
|
/// The base address for the ARM generic timer, IRQs, mailboxes
|
||||||
|
const GEN_TIMER_REG_BASE: usize = 0x40000000;
|
||||||
|
|
||||||
|
/// Core interrupt sources (ref: QA7 4.10, page 16)
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
enum CoreInterrupt {
|
||||||
|
CNTPSIRQ = 0,
|
||||||
|
CNTPNSIRQ = 1,
|
||||||
|
CNTHPIRQ = 2,
|
||||||
|
CNTVIRQ = 3,
|
||||||
|
Mailbox0 = 4,
|
||||||
|
Mailbox1 = 5,
|
||||||
|
Mailbox2 = 6,
|
||||||
|
Mailbox3 = 7,
|
||||||
|
Gpu = 8,
|
||||||
|
Pmu = 9,
|
||||||
|
AxiOutstanding = 10,
|
||||||
|
LocalTimer = 11,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timer, IRQs, mailboxes registers (ref: QA7 chapter 4, page 7)
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Registers {
|
||||||
|
CONTROL: Volatile<u32>,
|
||||||
|
_unused1: [Volatile<u32>; 8],
|
||||||
|
LOCAL_IRQ: Volatile<u32>,
|
||||||
|
_unused2: [Volatile<u32>; 3],
|
||||||
|
LOCAL_TIMER_CTL: Volatile<u32>,
|
||||||
|
LOCAL_TIMER_FLAGS: Volatile<u32>,
|
||||||
|
_unused3: Volatile<u32>,
|
||||||
|
CORE_TIMER_IRQCNTL: [Volatile<u32>; 4],
|
||||||
|
CORE_MAILBOX_IRQCNTL: [Volatile<u32>; 4],
|
||||||
|
CORE_IRQ_SRC: [Volatile<u32>; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ARM generic timer.
|
||||||
|
pub struct Timer {
|
||||||
|
registers: &'static mut Registers,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
/// Returns a new instance of `Timer`.
|
||||||
|
pub fn new() -> Timer {
|
||||||
|
Timer {
|
||||||
|
registers: unsafe { &mut *(GEN_TIMER_REG_BASE as *mut Registers) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the generic timer's counter and returns the 64-bit counter value.
|
||||||
|
/// The returned value is the number of elapsed microseconds.
|
||||||
|
pub fn read(&self) -> u64 {
|
||||||
|
let cntfrq = CNTFRQ_EL0.get();
|
||||||
|
(CNTPCT_EL0.get() * 1000000 / (cntfrq as u64)) as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up a match in timer 1 to occur `us` microseconds from now. If
|
||||||
|
/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer
|
||||||
|
/// interrupt will be issued in `us` microseconds.
|
||||||
|
pub fn tick_in(&mut self, us: u32) {
|
||||||
|
let cntfrq = CNTFRQ_EL0.get();
|
||||||
|
CNTP_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialization timer
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
self.registers.CORE_TIMER_IRQCNTL[0].write(1 << (CoreInterrupt::CNTPNSIRQ as u8));
|
||||||
|
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if timer interruption is pending. Otherwise, returns `false`.
|
||||||
|
pub fn is_pending(&self) -> bool {
|
||||||
|
self.registers.CORE_IRQ_SRC[0].read() & (1 << (CoreInterrupt::CNTPNSIRQ as u8)) != 0
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
#[cfg(feature = "use_generic_timer")]
|
||||||
|
mod generic_timer;
|
||||||
|
#[cfg(feature = "use_generic_timer")]
|
||||||
|
pub use self::generic_timer::Timer;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "use_generic_timer"))]
|
||||||
|
mod system_timer;
|
||||||
|
#[cfg(not(feature = "use_generic_timer"))]
|
||||||
|
pub use self::system_timer::Timer;
|
||||||
|
|
||||||
|
/// Initialization timer
|
||||||
|
pub fn init() {
|
||||||
|
Timer::new().init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current time in microseconds.
|
||||||
|
pub fn current_time() -> u64 {
|
||||||
|
Timer::new().read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up a match in timer 1 to occur `us` microseconds from now. If
|
||||||
|
/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer
|
||||||
|
/// interrupt will be issued in `us` microseconds.
|
||||||
|
pub fn tick_in(us: u32) {
|
||||||
|
Timer::new().tick_in(us);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// wait for `cycle` CPU cycles
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn delay(cycle: u32) {
|
||||||
|
for _ in 0..cycle {
|
||||||
|
unsafe { asm!("nop") }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
use ::IO_BASE;
|
||||||
|
use volatile::{ReadOnly, Volatile};
|
||||||
|
use interrupt::{Controller, Interrupt};
|
||||||
|
|
||||||
|
/// The base address for the ARM system timer registers.
|
||||||
|
const TIMER_REG_BASE: usize = IO_BASE + 0x3000;
|
||||||
|
|
||||||
|
/// System timer registers (ref: peripherals 12.1, page 172)
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct Registers {
|
||||||
|
CS: Volatile<u32>,
|
||||||
|
CLO: ReadOnly<u32>,
|
||||||
|
CHI: ReadOnly<u32>,
|
||||||
|
COMPARE: [Volatile<u32>; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
enum SystemTimer {
|
||||||
|
Timer0 = 0,
|
||||||
|
Timer1 = 1,
|
||||||
|
Timer2 = 2,
|
||||||
|
Timer3 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Raspberry Pi ARM system timer.
|
||||||
|
pub struct Timer {
|
||||||
|
registers: &'static mut Registers,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
/// Returns a new instance of `Timer`.
|
||||||
|
pub fn new() -> Timer {
|
||||||
|
Timer {
|
||||||
|
registers: unsafe { &mut *(TIMER_REG_BASE as *mut Registers) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the system timer's counter and returns the 64-bit counter value.
|
||||||
|
/// The returned value is the number of elapsed microseconds.
|
||||||
|
pub fn read(&self) -> u64 {
|
||||||
|
let low = self.registers.CLO.read();
|
||||||
|
let high = self.registers.CHI.read();
|
||||||
|
((high as u64) << 32) | (low as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up a match in timer 1 to occur `us` microseconds from now. If
|
||||||
|
/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer
|
||||||
|
/// interrupt will be issued in `us` microseconds.
|
||||||
|
pub fn tick_in(&mut self, us: u32) {
|
||||||
|
let current_low = self.registers.CLO.read();
|
||||||
|
let compare = current_low.wrapping_add(us);
|
||||||
|
self.registers.COMPARE[SystemTimer::Timer1 as usize].write(compare);
|
||||||
|
self.registers.CS.write(1 << (SystemTimer::Timer1 as usize)); // unmask
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialization timer
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
Controller::new().enable(Interrupt::Timer1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if timer interruption is pending. Otherwise, returns `false`.
|
||||||
|
pub fn is_pending(&self) -> bool {
|
||||||
|
let controller = Controller::new();
|
||||||
|
controller.is_pending(Interrupt::Timer1)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"abi-blacklist": [
|
||||||
|
"stdcall",
|
||||||
|
"fastcall",
|
||||||
|
"vectorcall",
|
||||||
|
"thiscall",
|
||||||
|
"win64",
|
||||||
|
"sysv64"
|
||||||
|
],
|
||||||
|
"arch": "aarch64",
|
||||||
|
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||||
|
"executables": true,
|
||||||
|
"linker": "rust-lld",
|
||||||
|
"linker-flavor": "ld.lld",
|
||||||
|
"linker-is-gnu": true,
|
||||||
|
"pre-link-args": {
|
||||||
|
"ld.lld": [
|
||||||
|
"-Tsrc/arch/aarch64/boot/linker.ld"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"llvm-target": "aarch64-unknown-none",
|
||||||
|
"no-compiler-rt": true,
|
||||||
|
"features": "+a53,+strict-align,-neon",
|
||||||
|
"max-atomic-width": 128,
|
||||||
|
"os": "none",
|
||||||
|
"panic": "abort",
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"relocation-model": "static",
|
||||||
|
"position-independent-executables": true,
|
||||||
|
"target-c-int-width": "32",
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": "64",
|
||||||
|
"target-family": "unix",
|
||||||
|
"disable-redzone": true
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
use arch::interrupt::TrapFrame;
|
||||||
|
use super::bcm2837::timer::Timer;
|
||||||
|
use super::bcm2837::interrupt::{Controller, Interrupt};
|
||||||
|
|
||||||
|
pub fn handle_irq(tf: &mut TrapFrame) {
|
||||||
|
let controller = Timer::new();
|
||||||
|
if controller.is_pending() {
|
||||||
|
super::timer::set_next();
|
||||||
|
::trap::timer();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
//! Raspberry PI 3 Model B/B+
|
||||||
|
|
||||||
|
extern crate bcm2837;
|
||||||
|
|
||||||
|
pub mod irq;
|
||||||
|
pub mod timer;
|
||||||
|
pub mod serial;
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
// FIXME
|
||||||
|
// assert_has_not_been_called!("board::init must be called only once");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
serial::SERIAL_PORT.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Hello Raspberry Pi!");
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
use super::bcm2837::mini_uart::MiniUart;
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
/// Struct to get a global SerialPort interface
|
||||||
|
pub struct SerialPort {
|
||||||
|
mu: Option<MiniUart>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SerialRead {
|
||||||
|
fn receive(&mut self) -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialPort {
|
||||||
|
/// Creates a new instance of `SerialPort`.
|
||||||
|
const fn new() -> SerialPort {
|
||||||
|
SerialPort { mu: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Init a newly created SerialPort, can only be called once.
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
// FIXME
|
||||||
|
// assert_has_not_been_called!("SerialPort::init must be called only once");
|
||||||
|
self.mu = Some(MiniUart::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the byte `byte` to the UART device.
|
||||||
|
pub fn write_byte(&mut self, byte: u8) {
|
||||||
|
match &mut self.mu {
|
||||||
|
Some(mu) => mu.write_byte(byte),
|
||||||
|
None => panic!("SerialPort is not initialized"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a byte from the UART device, blocking until a byte is available.
|
||||||
|
pub fn read_byte(&mut self) -> u8 {
|
||||||
|
match &mut self.mu {
|
||||||
|
Some(mu) => return mu.read_byte(),
|
||||||
|
None => panic!("SerialPort is not initialized"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialRead for SerialPort {
|
||||||
|
fn receive(&mut self) -> u8 {
|
||||||
|
self.read_byte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for SerialPort {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for byte in s.bytes() {
|
||||||
|
match byte {
|
||||||
|
// Backspace
|
||||||
|
b'\x7f' => {
|
||||||
|
self.write_byte(b'\x08');
|
||||||
|
self.write_byte(b' ');
|
||||||
|
self.write_byte(b'\x08');
|
||||||
|
}
|
||||||
|
// Return
|
||||||
|
b'\n' => {
|
||||||
|
self.write_byte(b'\r');
|
||||||
|
self.write_byte(b'\n');
|
||||||
|
}
|
||||||
|
// Others
|
||||||
|
_ => self.write_byte(byte),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
// pub static SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||||
|
pub static mut SERIAL_PORT: SerialPort = SerialPort::new();
|
||||||
|
|
@ -0,0 +1,17 @@
|
|||||||
|
use super::bcm2837::timer;
|
||||||
|
use super::bcm2837::interrupt::{Controller, Interrupt};
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
timer::init();
|
||||||
|
set_next();
|
||||||
|
info!("timer: init end");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cycle() -> u64 {
|
||||||
|
timer::current_time()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_next() {
|
||||||
|
// 10 ms
|
||||||
|
timer::tick_in(10 * 1000);
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
# TODO rewrite in Rust, use crate cortex-a
|
||||||
|
|
||||||
|
.section .text.boot
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
# read cpu affinity, start core 0, halt rest
|
||||||
|
mrs x1, mpidr_el1
|
||||||
|
and x1, x1, #3
|
||||||
|
cbz x1, setup
|
||||||
|
|
||||||
|
halt:
|
||||||
|
# core affinity != 0, halt it
|
||||||
|
wfe
|
||||||
|
b halt
|
||||||
|
|
||||||
|
setup:
|
||||||
|
# store the desired EL1 stack pointer in x1
|
||||||
|
adr x1, _start
|
||||||
|
|
||||||
|
# use SP_ELx for Exception level ELx
|
||||||
|
msr SPsel, #1
|
||||||
|
|
||||||
|
# read the current exception level into x0 (ref: C5.2.1)
|
||||||
|
mrs x0, CurrentEL
|
||||||
|
and x0, x0, #0b1100
|
||||||
|
lsr x0, x0, #2
|
||||||
|
|
||||||
|
switch_to_el2:
|
||||||
|
# switch to EL2 if we're in EL3. otherwise switch to EL1
|
||||||
|
cmp x0, #2
|
||||||
|
beq switch_to_el1
|
||||||
|
|
||||||
|
# set-up SCR_EL3 (bits 0, 4, 5, 7, 8, 10) (A53: 4.3.42)
|
||||||
|
mov x0, #0x5b1
|
||||||
|
msr scr_el3, x0
|
||||||
|
|
||||||
|
# set-up SPSR_EL3 (bits 0, 3, 6, 7, 8, 9) (ref: C5.2.20)
|
||||||
|
mov x0, #0x3c9
|
||||||
|
msr spsr_el3, x0
|
||||||
|
|
||||||
|
# switch
|
||||||
|
adr x0, switch_to_el1
|
||||||
|
msr elr_el3, x0
|
||||||
|
|
||||||
|
eret
|
||||||
|
|
||||||
|
switch_to_el1:
|
||||||
|
# switch to EL1 if we're not already in EL1. otherwise continue with start
|
||||||
|
cmp x0, #1
|
||||||
|
beq set_stack
|
||||||
|
|
||||||
|
# set the stack-pointer for EL1
|
||||||
|
msr sp_el1, x1
|
||||||
|
|
||||||
|
# set-up HCR_EL2, enable AArch64 in EL1 (bits 1, 31) (ref: D10.2.45)
|
||||||
|
mov x0, #0x0002
|
||||||
|
movk x0, #0x8000, lsl #16
|
||||||
|
msr hcr_el2, x0
|
||||||
|
|
||||||
|
# don't trap accessing SVE registers (ref: D10.2.30)
|
||||||
|
msr cptr_el2, xzr
|
||||||
|
|
||||||
|
# enable floating point and SVE (SIMD) (bits 20, 21) (ref: D10.2.29)
|
||||||
|
mrs x0, cpacr_el1
|
||||||
|
orr x0, x0, #(0x3 << 20)
|
||||||
|
msr cpacr_el1, x0
|
||||||
|
|
||||||
|
# Set SCTLR to known state (RES1: 11, 20, 22, 23, 28, 29) (ref: D10.2.100)
|
||||||
|
mov x0, #0x0800
|
||||||
|
movk x0, #0x30d0, lsl #16
|
||||||
|
msr sctlr_el1, x0
|
||||||
|
|
||||||
|
# set-up SPSR_EL2 (bits 0, 2, 6, 7, 8, 9) (ref: C5.2.19)
|
||||||
|
mov x0, #0x3c5
|
||||||
|
msr spsr_el2, x0
|
||||||
|
|
||||||
|
# enable CNTP for EL1/EL0 (ref: D7.5.2, D7.5.13)
|
||||||
|
# NOTE: This doesn't actually enable the counter stream.
|
||||||
|
mrs x0, cnthctl_el2
|
||||||
|
orr x0, x0, #3
|
||||||
|
msr cnthctl_el2, x0
|
||||||
|
msr cntvoff_el2, xzr
|
||||||
|
|
||||||
|
# switch
|
||||||
|
adr x0, set_stack
|
||||||
|
msr elr_el2, x0
|
||||||
|
|
||||||
|
eret
|
||||||
|
|
||||||
|
set_stack:
|
||||||
|
# set the current stack pointer
|
||||||
|
mov sp, x1
|
||||||
|
|
||||||
|
zero_bss:
|
||||||
|
# load the start address and number of bytes in BSS section
|
||||||
|
ldr x1, =__bss_start
|
||||||
|
ldr x2, =__bss_length
|
||||||
|
|
||||||
|
zero_bss_loop:
|
||||||
|
# zero out the BSS section, 64-bits at a time
|
||||||
|
cbz x2, go_kmain
|
||||||
|
str xzr, [x1], #8
|
||||||
|
sub x2, x2, #8
|
||||||
|
cbnz x2, zero_bss_loop
|
||||||
|
|
||||||
|
go_kmain:
|
||||||
|
# jump to rust_main, which shouldn't return. halt if it does
|
||||||
|
bl rust_main
|
||||||
|
b halt
|
@ -0,0 +1,39 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
. = 0x80000; /* Raspbery Pi 3 Aarch64 (kernel8.img) load address */
|
||||||
|
|
||||||
|
/* start of the binary */
|
||||||
|
_start = .;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
KEEP(*(.text.boot)) /* from boot.S */
|
||||||
|
*(.text .text.* .gnu.linkonce.t*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
*(.rodata .rodata.* .gnu.linkonce.r*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
*(.data .data.* .gnu.linkonce.d*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
. = ALIGN(32);
|
||||||
|
__bss_start = .;
|
||||||
|
*(.bss .bss.*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(8);
|
||||||
|
__bss_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of the binary */
|
||||||
|
_end = ALIGN(8);
|
||||||
|
|
||||||
|
/* number of bytes in BSS section and complete binary */
|
||||||
|
__bss_length = (__bss_end - __bss_start);
|
||||||
|
__binary_length = (_end - _start);
|
||||||
|
|
||||||
|
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
//! TODO: replace unmiplemented consts with real value
|
||||||
|
const UNIMPLEMENTED: usize = 0;
|
||||||
|
pub const KERNEL_OFFSET: usize = UNIMPLEMENTED;
|
||||||
|
pub const KERNEL_PML4: usize = UNIMPLEMENTED;
|
||||||
|
pub const KERNEL_HEAP_OFFSET: usize = UNIMPLEMENTED;
|
||||||
|
pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024;
|
||||||
|
pub const MEMORY_OFFSET: usize = UNIMPLEMENTED;
|
||||||
|
pub const MEMORY_END: usize = UNIMPLEMENTED;
|
||||||
|
pub const USER_STACK_OFFSET: usize = UNIMPLEMENTED;
|
||||||
|
pub const USER_STACK_SIZE: usize = UNIMPLEMENTED;
|
||||||
|
pub const USER32_STACK_OFFSET: usize = UNIMPLEMENTED;
|
@ -0,0 +1,151 @@
|
|||||||
|
//! TrapFrame and context definitions for aarch64.
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Copy, Clone)]
|
||||||
|
pub struct TrapFrame {
|
||||||
|
pub elr: usize,
|
||||||
|
pub spsr: usize,
|
||||||
|
pub sp: usize,
|
||||||
|
pub tpidr: usize,
|
||||||
|
// pub q0to31: [u128; 32], // disable SIMD/FP registers
|
||||||
|
pub x1to29: [usize; 29],
|
||||||
|
pub __reserved: usize,
|
||||||
|
pub x30: usize, // lr
|
||||||
|
pub x0: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 用于在内核栈中构造新线程的中断帧
|
||||||
|
impl TrapFrame {
|
||||||
|
fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, sp: usize) -> Self {
|
||||||
|
use core::mem::zeroed;
|
||||||
|
let mut tf: Self = unsafe { zeroed() };
|
||||||
|
tf.x0 = arg;
|
||||||
|
tf.sp = sp;
|
||||||
|
tf.elr = entry as usize;
|
||||||
|
tf.spsr = 0b1101_00_0101; // To EL 1, enable IRQ
|
||||||
|
tf
|
||||||
|
}
|
||||||
|
fn new_user_thread(entry_addr: usize, sp: usize) -> Self {
|
||||||
|
use core::mem::zeroed;
|
||||||
|
let mut tf: Self = unsafe { zeroed() };
|
||||||
|
tf.sp = sp;
|
||||||
|
tf.elr = entry_addr;
|
||||||
|
tf.spsr = 0b1101_00_0000; // To EL 0, enable IRQ
|
||||||
|
tf
|
||||||
|
}
|
||||||
|
pub fn is_user(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 新线程的内核栈初始内容
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct InitStack {
|
||||||
|
context: ContextData,
|
||||||
|
tf: TrapFrame,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InitStack {
|
||||||
|
unsafe fn push_at(self, stack_top: usize) -> Context {
|
||||||
|
let ptr = (stack_top as *mut Self).offset(-1);
|
||||||
|
*ptr = self;
|
||||||
|
Context(ptr as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern {
|
||||||
|
fn __trapret();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct ContextData {
|
||||||
|
x19to29: [usize; 11],
|
||||||
|
lr: usize,
|
||||||
|
ttbr0: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextData {
|
||||||
|
fn new(ttbr0: usize) -> Self {
|
||||||
|
ContextData { lr: __trapret as usize, ttbr0, ..ContextData::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Context(usize);
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
/// Switch to another kernel thread.
|
||||||
|
///
|
||||||
|
/// Defined in `trap.S`.
|
||||||
|
///
|
||||||
|
/// Push all callee-saved registers at the current kernel stack.
|
||||||
|
/// Store current sp, switch to target.
|
||||||
|
/// Pop all callee-saved registers, then return to the target.
|
||||||
|
#[naked]
|
||||||
|
#[inline(never)]
|
||||||
|
pub unsafe extern fn switch(&mut self, target: &mut Self) {
|
||||||
|
asm!(
|
||||||
|
"
|
||||||
|
mov x10, #-(13 * 8)
|
||||||
|
add x8, sp, x10
|
||||||
|
str x8, [x0]
|
||||||
|
stp x19, x20, [x8], #16 // store callee-saved registers
|
||||||
|
stp x21, x22, [x8], #16
|
||||||
|
stp x23, x24, [x8], #16
|
||||||
|
stp x25, x26, [x8], #16
|
||||||
|
stp x27, x28, [x8], #16
|
||||||
|
stp x29, lr, [x8], #16
|
||||||
|
mrs x9, ttbr0_el1
|
||||||
|
str x9, [x8], #8
|
||||||
|
|
||||||
|
ldr x8, [x1]
|
||||||
|
ldp x19, x20, [x8], #16 // restore callee-saved registers
|
||||||
|
ldp x21, x22, [x8], #16
|
||||||
|
ldp x23, x24, [x8], #16
|
||||||
|
ldp x25, x26, [x8], #16
|
||||||
|
ldp x27, x28, [x8], #16
|
||||||
|
ldp x29, lr, [x8], #16
|
||||||
|
ldr x9, [x8], #8
|
||||||
|
mov sp, x8
|
||||||
|
|
||||||
|
msr ttbr0_el1, x9 // set new page directory
|
||||||
|
dsb ishst // ensure write has completed
|
||||||
|
tlbi vmalle1is // invalidate the TLB entry for the entry that changes
|
||||||
|
dsb ish // ensure TLB invalidation is complete
|
||||||
|
isb // synchronize context on this processor
|
||||||
|
|
||||||
|
str xzr, [x1]
|
||||||
|
ret"
|
||||||
|
: : : : "volatile" );
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn null() -> Self {
|
||||||
|
Context(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, ttbr0: usize) -> Self {
|
||||||
|
InitStack {
|
||||||
|
context: ContextData::new(ttbr0),
|
||||||
|
tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top),
|
||||||
|
}.push_at(kstack_top)
|
||||||
|
}
|
||||||
|
pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, is32: bool, ttbr0: usize) -> Self {
|
||||||
|
InitStack {
|
||||||
|
context: ContextData::new(ttbr0),
|
||||||
|
tf: TrapFrame::new_user_thread(entry_addr, ustack_top),
|
||||||
|
}.push_at(kstack_top)
|
||||||
|
}
|
||||||
|
pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, ttbr0: usize) -> Self {
|
||||||
|
InitStack {
|
||||||
|
context: ContextData::new(ttbr0),
|
||||||
|
tf: {
|
||||||
|
let mut tf = tf.clone();
|
||||||
|
tf.x0 = 0;
|
||||||
|
tf
|
||||||
|
},
|
||||||
|
}.push_at(kstack_top)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
//! Trap handler
|
||||||
|
|
||||||
|
use arch::board::irq::handle_irq;
|
||||||
|
use super::context::TrapFrame;
|
||||||
|
use super::syndrome::Syndrome;
|
||||||
|
|
||||||
|
global_asm!(include_str!("trap.S"));
|
||||||
|
global_asm!(include_str!("vector.S"));
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum Kind {
|
||||||
|
Synchronous = 0,
|
||||||
|
Irq = 1,
|
||||||
|
Fiq = 2,
|
||||||
|
SError = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum Source {
|
||||||
|
CurrentSpEl0 = 0,
|
||||||
|
CurrentSpElx = 1,
|
||||||
|
LowerAArch64 = 2,
|
||||||
|
LowerAArch32 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct Info {
|
||||||
|
source: Source,
|
||||||
|
kind: Kind,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function is called when an exception occurs. The `info` parameter
|
||||||
|
/// specifies the source and kind of exception that has occurred. The `esr` is
|
||||||
|
/// the value of the exception syndrome register. Finally, `tf` is a pointer to
|
||||||
|
/// the trap frame for the exception.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) {
|
||||||
|
let syndrome = Syndrome::from(esr);
|
||||||
|
trace!("Interrupt: {:?} from: {:?}", syndrome, info);
|
||||||
|
match info.kind {
|
||||||
|
Kind::Synchronous => {
|
||||||
|
// syndrome is only valid with sync
|
||||||
|
match syndrome {
|
||||||
|
Syndrome::Brk(brk) => handle_break(brk, tf),
|
||||||
|
Syndrome::Svc(_) => handle_syscall(tf),
|
||||||
|
_ => ::trap::error(tf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Kind::Irq => handle_irq(tf),
|
||||||
|
_ => ::trap::error(tf),
|
||||||
|
}
|
||||||
|
::trap::before_return();
|
||||||
|
trace!("Interrupt end");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_break(num: u16, tf: &mut TrapFrame) {
|
||||||
|
// Skip the current brk instruction (ref: J1.1.2, page 6147)
|
||||||
|
tf.elr += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_syscall(tf: &mut TrapFrame) {
|
||||||
|
// svc instruction has been skipped in syscall (ref: J1.1.2, page 6152)
|
||||||
|
let ret = ::syscall::syscall(
|
||||||
|
tf.x1to29[7] as usize,
|
||||||
|
[
|
||||||
|
tf.x0,
|
||||||
|
tf.x1to29[0],
|
||||||
|
tf.x1to29[1],
|
||||||
|
tf.x1to29[2],
|
||||||
|
tf.x1to29[3],
|
||||||
|
tf.x1to29[4],
|
||||||
|
],
|
||||||
|
tf,
|
||||||
|
);
|
||||||
|
tf.x0 = ret as usize;
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
//! Interrupt and exception for aarch64.
|
||||||
|
|
||||||
|
mod handler;
|
||||||
|
mod context;
|
||||||
|
mod syndrome;
|
||||||
|
|
||||||
|
use super::cortex_a::regs::*;
|
||||||
|
pub use self::context::*;
|
||||||
|
pub use self::handler::*;
|
||||||
|
|
||||||
|
/// Set the exception vector address
|
||||||
|
pub fn init() {
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"adr x0, __vectors;
|
||||||
|
msr vbar_el1, x0"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the interrupt (only IRQ).
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn enable() {
|
||||||
|
asm!("msr daifclr, #2");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the interrupt (only IRQ).
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn disable() {
|
||||||
|
asm!("msr daifset, #2");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the interrupt and store the status.
|
||||||
|
///
|
||||||
|
/// return: status(usize)
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn disable_and_store() -> usize {
|
||||||
|
let daif = DAIF.get() as usize;
|
||||||
|
disable();
|
||||||
|
daif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use the original status to restore the process
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * flags: original status(usize)
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn restore(flags: usize) {
|
||||||
|
DAIF.set(flags as u32);
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
//! Exception syndrome from ESR
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
pub enum Fault {
|
||||||
|
AddressSize,
|
||||||
|
Translation,
|
||||||
|
AccessFlag,
|
||||||
|
Permission,
|
||||||
|
Alignment,
|
||||||
|
TlbConflict,
|
||||||
|
Other(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Fault {
|
||||||
|
fn from(val: u32) -> Fault {
|
||||||
|
use self::Fault::*;
|
||||||
|
|
||||||
|
// IFSC or DFSC bits (ref: D10.2.39, Page 2457~2464).
|
||||||
|
match val & 0b111100 {
|
||||||
|
0b000000 => AddressSize,
|
||||||
|
0b000100 => Translation,
|
||||||
|
0b001000 => AccessFlag,
|
||||||
|
0b001100 => Permission,
|
||||||
|
0b100000 => Alignment,
|
||||||
|
0b110000 => TlbConflict,
|
||||||
|
_ => Other((val & 0b111111) as u8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
pub enum Syndrome {
|
||||||
|
Unknown,
|
||||||
|
WfiWfe,
|
||||||
|
McrMrc,
|
||||||
|
McrrMrrc,
|
||||||
|
LdcStc,
|
||||||
|
SimdFp,
|
||||||
|
Vmrs,
|
||||||
|
Mrrc,
|
||||||
|
IllegalExecutionState,
|
||||||
|
Svc(u16),
|
||||||
|
Hvc(u16),
|
||||||
|
Smc(u16),
|
||||||
|
MsrMrsSystem,
|
||||||
|
InstructionAbort { kind: Fault, level: u8 },
|
||||||
|
PCAlignmentFault,
|
||||||
|
DataAbort { kind: Fault, level: u8 },
|
||||||
|
SpAlignmentFault,
|
||||||
|
TrappedFpu,
|
||||||
|
SError,
|
||||||
|
Breakpoint,
|
||||||
|
Step,
|
||||||
|
Watchpoint,
|
||||||
|
Brk(u16),
|
||||||
|
Other(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a raw syndrome value (ESR) into a `Syndrome` (ref: D1.10.4, D10.2.39).
|
||||||
|
impl From<u32> for Syndrome {
|
||||||
|
fn from(esr: u32) -> Syndrome {
|
||||||
|
use self::Syndrome::*;
|
||||||
|
|
||||||
|
let ec = esr >> 26;
|
||||||
|
let iss = esr & 0xFFFFFF;
|
||||||
|
|
||||||
|
match ec {
|
||||||
|
0b000000 => Unknown,
|
||||||
|
0b000001 => WfiWfe,
|
||||||
|
0b000011 => McrMrc,
|
||||||
|
0b000100 => McrrMrrc,
|
||||||
|
0b000101 => McrMrc,
|
||||||
|
0b000110 => LdcStc,
|
||||||
|
0b000111 => SimdFp,
|
||||||
|
0b001000 => Vmrs,
|
||||||
|
0b001100 => Mrrc,
|
||||||
|
0b001110 => IllegalExecutionState,
|
||||||
|
0b010001 => Svc((iss & 0xFFFF) as u16),
|
||||||
|
0b010010 => Hvc((iss & 0xFFFF) as u16),
|
||||||
|
0b010011 => Smc((iss & 0xFFFF) as u16),
|
||||||
|
0b010101 => Svc((iss & 0xFFFF) as u16),
|
||||||
|
0b010110 => Hvc((iss & 0xFFFF) as u16),
|
||||||
|
0b010111 => Smc((iss & 0xFFFF) as u16),
|
||||||
|
0b011000 => MsrMrsSystem,
|
||||||
|
0b100000 | 0b100001 => InstructionAbort {
|
||||||
|
kind: Fault::from(iss),
|
||||||
|
level: (iss & 0b11) as u8,
|
||||||
|
},
|
||||||
|
0b100010 => PCAlignmentFault,
|
||||||
|
0b100100 | 0b100101 => DataAbort {
|
||||||
|
kind: Fault::from(iss),
|
||||||
|
level: (iss & 0b11) as u8,
|
||||||
|
},
|
||||||
|
0b100110 => SpAlignmentFault,
|
||||||
|
0b101000 => TrappedFpu,
|
||||||
|
0b101100 => TrappedFpu,
|
||||||
|
0b101111 => SError,
|
||||||
|
0b110000 => Breakpoint,
|
||||||
|
0b110001 => Breakpoint,
|
||||||
|
0b110010 => Step,
|
||||||
|
0b110011 => Step,
|
||||||
|
0b110100 => Watchpoint,
|
||||||
|
0b110101 => Watchpoint,
|
||||||
|
0b111100 => Brk((iss & 0xFFFF) as u16),
|
||||||
|
other => Other(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
.section .text
|
||||||
|
|
||||||
|
.macro SAVE_ALL
|
||||||
|
# lr, x0 is saved in HANDLER
|
||||||
|
str x29, [sp, #-16]!
|
||||||
|
stp x27, x28, [sp, #-16]!
|
||||||
|
stp x25, x26, [sp, #-16]!
|
||||||
|
stp x23, x24, [sp, #-16]!
|
||||||
|
stp x21, x22, [sp, #-16]!
|
||||||
|
stp x19, x20, [sp, #-16]!
|
||||||
|
stp x17, x18, [sp, #-16]!
|
||||||
|
stp x15, x16, [sp, #-16]!
|
||||||
|
stp x13, x14, [sp, #-16]!
|
||||||
|
stp x11, x12, [sp, #-16]!
|
||||||
|
stp x9, x10, [sp, #-16]!
|
||||||
|
stp x7, x8, [sp, #-16]!
|
||||||
|
stp x5, x6, [sp, #-16]!
|
||||||
|
stp x3, x4, [sp, #-16]!
|
||||||
|
stp x1, x2, [sp, #-16]!
|
||||||
|
|
||||||
|
# stp q30, q31, [sp, #-32]!
|
||||||
|
# stp q28, q29, [sp, #-32]!
|
||||||
|
# stp q26, q27, [sp, #-32]!
|
||||||
|
# stp q24, q25, [sp, #-32]!
|
||||||
|
# stp q22, q23, [sp, #-32]!
|
||||||
|
# stp q20, q21, [sp, #-32]!
|
||||||
|
# stp q18, q19, [sp, #-32]!
|
||||||
|
# stp q16, q17, [sp, #-32]!
|
||||||
|
# stp q14, q15, [sp, #-32]!
|
||||||
|
# stp q12, q13, [sp, #-32]!
|
||||||
|
# stp q10, q11, [sp, #-32]!
|
||||||
|
# stp q8, q9, [sp, #-32]!
|
||||||
|
# stp q6, q7, [sp, #-32]!
|
||||||
|
# stp q4, q5, [sp, #-32]!
|
||||||
|
# stp q2, q3, [sp, #-32]!
|
||||||
|
# stp q0, q1, [sp, #-32]!
|
||||||
|
|
||||||
|
mrs x2, tpidr_el0
|
||||||
|
mrs x1, sp_el0
|
||||||
|
stp x1, x2, [sp, #-16]!
|
||||||
|
|
||||||
|
mrs x2, spsr_el1
|
||||||
|
mrs x1, elr_el1
|
||||||
|
stp x1, x2, [sp, #-16]!
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro RESTORE_ALL
|
||||||
|
ldp x1, x2, [sp], #16
|
||||||
|
msr elr_el1, x1
|
||||||
|
msr spsr_el1, x2
|
||||||
|
|
||||||
|
ldp x1, x2, [sp], #16
|
||||||
|
msr sp_el0, x1
|
||||||
|
msr tpidr_el0, x2
|
||||||
|
|
||||||
|
# ldp q0, q1, [sp], #32
|
||||||
|
# ldp q2, q3, [sp], #32
|
||||||
|
# ldp q4, q5, [sp], #32
|
||||||
|
# ldp q6, q7, [sp], #32
|
||||||
|
# ldp q8, q9, [sp], #32
|
||||||
|
# ldp q10, q11, [sp], #32
|
||||||
|
# ldp q12, q13, [sp], #32
|
||||||
|
# ldp q14, q15, [sp], #32
|
||||||
|
# ldp q16, q17, [sp], #32
|
||||||
|
# ldp q18, q19, [sp], #32
|
||||||
|
# ldp q20, q21, [sp], #32
|
||||||
|
# ldp q22, q23, [sp], #32
|
||||||
|
# ldp q24, q25, [sp], #32
|
||||||
|
# ldp q26, q27, [sp], #32
|
||||||
|
# ldp q28, q29, [sp], #32
|
||||||
|
# ldp q30, q31, [sp], #32
|
||||||
|
|
||||||
|
ldp x1, x2, [sp], #16
|
||||||
|
ldp x3, x4, [sp], #16
|
||||||
|
ldp x5, x6, [sp], #16
|
||||||
|
ldp x7, x8, [sp], #16
|
||||||
|
ldp x9, x10, [sp], #16
|
||||||
|
ldp x11, x12, [sp], #16
|
||||||
|
ldp x13, x14, [sp], #16
|
||||||
|
ldp x15, x16, [sp], #16
|
||||||
|
ldp x17, x18, [sp], #16
|
||||||
|
ldp x19, x20, [sp], #16
|
||||||
|
ldp x21, x22, [sp], #16
|
||||||
|
ldp x23, x24, [sp], #16
|
||||||
|
ldp x25, x26, [sp], #16
|
||||||
|
ldp x27, x28, [sp], #16
|
||||||
|
ldr x29, [sp], #16
|
||||||
|
ldp lr, x0, [sp], #16
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.global __alltraps
|
||||||
|
__alltraps:
|
||||||
|
SAVE_ALL
|
||||||
|
|
||||||
|
# x0 is set in HANDLER
|
||||||
|
mrs x1, esr_el1
|
||||||
|
mov x2, sp
|
||||||
|
bl rust_trap
|
||||||
|
|
||||||
|
.global __trapret
|
||||||
|
__trapret:
|
||||||
|
RESTORE_ALL
|
||||||
|
eret
|
@ -0,0 +1,29 @@
|
|||||||
|
.section .text
|
||||||
|
|
||||||
|
.macro HANDLER source kind
|
||||||
|
.align 7
|
||||||
|
stp lr, x0, [sp, #-16]!
|
||||||
|
mov x0, #\source
|
||||||
|
movk x0, #\kind, lsl #16
|
||||||
|
b __alltraps
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.global __vectors
|
||||||
|
.align 11
|
||||||
|
__vectors:
|
||||||
|
HANDLER 0 0
|
||||||
|
HANDLER 0 1
|
||||||
|
HANDLER 0 2
|
||||||
|
HANDLER 0 3
|
||||||
|
HANDLER 1 0
|
||||||
|
HANDLER 1 1
|
||||||
|
HANDLER 1 2
|
||||||
|
HANDLER 1 3
|
||||||
|
HANDLER 2 0
|
||||||
|
HANDLER 2 1
|
||||||
|
HANDLER 2 2
|
||||||
|
HANDLER 2 3
|
||||||
|
HANDLER 3 0
|
||||||
|
HANDLER 3 1
|
||||||
|
HANDLER 3 2
|
||||||
|
HANDLER 3 3
|
@ -0,0 +1,18 @@
|
|||||||
|
//! Serial driver for aarch64.
|
||||||
|
|
||||||
|
use core::fmt::{Arguments, Write};
|
||||||
|
use super::board::serial::{SerialRead, SERIAL_PORT};
|
||||||
|
|
||||||
|
pub fn getchar() -> char {
|
||||||
|
// FIXME
|
||||||
|
unsafe {
|
||||||
|
SERIAL_PORT.receive() as char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putfmt(fmt: Arguments) {
|
||||||
|
// FIXME
|
||||||
|
unsafe {
|
||||||
|
SERIAL_PORT.write_fmt(fmt).unwrap()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
//! Memory initialization for aarch64.
|
||||||
|
|
||||||
|
use ucore_memory::PAGE_SIZE;
|
||||||
|
use super::atags::atags::Atags;
|
||||||
|
use super::super::HEAP_ALLOCATOR;
|
||||||
|
|
||||||
|
/// Memory initialization.
|
||||||
|
pub fn init() {
|
||||||
|
let (start, end) = memory_map().expect("failed to find memory map");
|
||||||
|
unsafe {
|
||||||
|
HEAP_ALLOCATOR.lock().init(start, end - start);
|
||||||
|
}
|
||||||
|
info!("memory: init end");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
static _end: u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the (start address, end address) of the available memory on this
|
||||||
|
/// system if it can be determined. If it cannot, `None` is returned.
|
||||||
|
///
|
||||||
|
/// This function is expected to return `Some` under all normal cirumstances.
|
||||||
|
pub fn memory_map() -> Option<(usize, usize)> {
|
||||||
|
let binary_end = unsafe { (&_end as *const u8) as u32 };
|
||||||
|
|
||||||
|
let mut atags: Atags = Atags::get();
|
||||||
|
while let Some(atag) = atags.next() {
|
||||||
|
if let Some(mem) = atag.mem() {
|
||||||
|
return Some((binary_end as usize, (mem.start + mem.size) as usize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
//! Entrance and initialization for aarch64.
|
||||||
|
|
||||||
|
extern crate atags;
|
||||||
|
extern crate cortex_a;
|
||||||
|
|
||||||
|
pub mod io;
|
||||||
|
pub mod paging;
|
||||||
|
pub mod memory;
|
||||||
|
pub mod interrupt;
|
||||||
|
pub mod consts;
|
||||||
|
|
||||||
|
#[cfg(feature = "board_raspi3")]
|
||||||
|
#[path = "board/raspi3/mod.rs"]
|
||||||
|
pub mod board;
|
||||||
|
|
||||||
|
pub use self::board::timer;
|
||||||
|
|
||||||
|
/// The entry point of kernel
|
||||||
|
#[no_mangle] // don't mangle the name of this function
|
||||||
|
pub extern "C" fn rust_main() -> ! {
|
||||||
|
// Init board to enable serial port.
|
||||||
|
board::init();
|
||||||
|
::logging::init(); // FIXME
|
||||||
|
interrupt::init();
|
||||||
|
memory::init();
|
||||||
|
timer::init();
|
||||||
|
::kmain();
|
||||||
|
}
|
||||||
|
|
||||||
|
global_asm!(include_str!("boot/boot.S"));
|
@ -0,0 +1,222 @@
|
|||||||
|
//! Page table implementations for aarch64.
|
||||||
|
|
||||||
|
use ucore_memory::memory_set::*;
|
||||||
|
use ucore_memory::paging::*;
|
||||||
|
|
||||||
|
type VirtAddr = usize;
|
||||||
|
type PhysAddr = usize;
|
||||||
|
|
||||||
|
use alloc::alloc::{alloc, Layout};
|
||||||
|
use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame};
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub struct ActivePageTable {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActivePageTable {
|
||||||
|
/// TODO
|
||||||
|
pub unsafe fn new() -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageTable for ActivePageTable {
|
||||||
|
type Entry = PageEntry;
|
||||||
|
|
||||||
|
fn map(&mut self, addr: VirtAddr, target: PhysAddr) -> &mut Self::Entry {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn unmap(&mut self, addr: VirtAddr) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_entry(&mut self, addr: VirtAddr) -> &mut Self::Entry {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing with mock
|
||||||
|
fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: VirtAddr) -> &'b mut [u8] {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, addr: VirtAddr) -> u8 {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, addr: VirtAddr, data: u8) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub struct PageEntry {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entry for PageEntry {
|
||||||
|
/// IMPORTANT!
|
||||||
|
/// This must be called after any change to ensure it become effective.
|
||||||
|
/// Usually this will make a flush to TLB/MMU.
|
||||||
|
fn update(&mut self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will be set when accessed
|
||||||
|
fn accessed(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will be set when written
|
||||||
|
fn dirty(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will PageFault when try to write page where writable=0
|
||||||
|
fn writable(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will PageFault when try to access page where present=0
|
||||||
|
fn present(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_accessed(&mut self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_dirty(&mut self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_writable(&mut self, value: bool) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_present(&mut self, value: bool) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target(&self) -> PhysAddr {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_target(&mut self, target: PhysAddr) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Copy-on-write extension
|
||||||
|
fn writable_shared(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readonly_shared(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_shared(&mut self, writable: bool) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_shared(&mut self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Swap extension
|
||||||
|
fn swapped(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_swapped(&mut self, value: bool) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_user(&mut self, value: bool) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_execute(&mut self, value: bool) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct MockFrame(PhysAddr);
|
||||||
|
|
||||||
|
impl MockFrame {
|
||||||
|
pub fn of_addr(addr: PhysAddr) -> Self {
|
||||||
|
MockFrame(addr)
|
||||||
|
}
|
||||||
|
pub fn start_address(&self) -> PhysAddr {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
pub fn p2_index(&self) -> usize {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
pub fn p1_index(&self) -> usize {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
pub fn number(&self) -> usize {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub struct InactivePageTable0 {
|
||||||
|
p4_frame: MockFrame,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
impl InactivePageTable for InactivePageTable0 {
|
||||||
|
type Active = ActivePageTable;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
unsafe {
|
||||||
|
let layout = Layout::new::<u64>();
|
||||||
|
let ptr = alloc(layout);
|
||||||
|
let frame = MockFrame::of_addr(*ptr as usize);
|
||||||
|
InactivePageTable0 { p4_frame: frame }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_bare() -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit(&mut self, f: impl FnOnce(&mut Self::Active)) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn activate(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn with(&self, f: impl FnOnce()) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn token(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_frame() -> Option<PhysAddr> {
|
||||||
|
alloc_frame()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dealloc_frame(target: PhysAddr) {
|
||||||
|
dealloc_frame(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_stack() -> Stack {
|
||||||
|
alloc_stack()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
target remote :1234
|
||||||
|
break rust_main
|
||||||
|
continue
|
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"abi-blacklist": [
|
||||||
|
"stdcall",
|
||||||
|
"fastcall",
|
||||||
|
"vectorcall",
|
||||||
|
"thiscall",
|
||||||
|
"win64",
|
||||||
|
"sysv64"
|
||||||
|
],
|
||||||
|
"arch": "aarch64",
|
||||||
|
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||||
|
"executables": true,
|
||||||
|
"linker": "rust-lld",
|
||||||
|
"linker-flavor": "ld.lld",
|
||||||
|
"linker-is-gnu": true,
|
||||||
|
"llvm-target": "aarch64-unknown-none",
|
||||||
|
"no-compiler-rt": true,
|
||||||
|
"features": "+a53,+strict-align,-neon",
|
||||||
|
"max-atomic-width": 128,
|
||||||
|
"os": "none",
|
||||||
|
"panic": "abort",
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"relocation-model": "static",
|
||||||
|
"position-independent-executables": true,
|
||||||
|
"target-c-int-width": "32",
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": "64",
|
||||||
|
"target-family": "unix",
|
||||||
|
"disable-redzone": true
|
||||||
|
}
|
Loading…
Reference in new issue