diff --git a/.gitignore b/.gitignore index 679723a0..fe8b1fd3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,9 @@ target /crate/memory/Cargo.lock /crate/bbl/Cargo.lock /crate/sync/Cargo.lock -/crate/process/Cargo.lock \ No newline at end of file +/crate/process/Cargo.lock + +.DS_Store + +# for eclipse +.project diff --git a/crate/atags/Cargo.lock b/crate/atags/Cargo.lock new file mode 100644 index 00000000..42d11ebd --- /dev/null +++ b/crate/atags/Cargo.lock @@ -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" diff --git a/crate/atags/Cargo.toml b/crate/atags/Cargo.toml new file mode 100644 index 00000000..3c095bfa --- /dev/null +++ b/crate/atags/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "atags" +version = "0.1.0" +authors = ["koumingyang <1761674434@qq.com>"] + +[dependencies] \ No newline at end of file diff --git a/crate/atags/src/atag.rs b/crate/atags/src/atag.rs new file mode 100644 index 00000000..77f729a7 --- /dev/null +++ b/crate/atags/src/atag.rs @@ -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 { + match self { + Atag::Core(x) => Some(x), + _ => None, + } + } + + /// Returns `Some` if this is a `Mem` ATAG. Otherwise returns `None`. + pub fn mem(self) -> Option { + 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), + } + } + } +} diff --git a/crate/atags/src/atags.rs b/crate/atags/src/atags.rs new file mode 100644 index 00000000..98b7522a --- /dev/null +++ b/crate/atags/src/atags.rs @@ -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 { + let cur = self.ptr; + match cur.next() { + Some(next) => { + let result = Some(Atag::from(cur)); + self.ptr = next; + result + }, + None => None, + } + } +} diff --git a/crate/atags/src/lib.rs b/crate/atags/src/lib.rs new file mode 100644 index 00000000..33c76c26 --- /dev/null +++ b/crate/atags/src/lib.rs @@ -0,0 +1,6 @@ +#![no_std] + +mod raw; +mod atag; + +pub mod atags; diff --git a/crate/atags/src/raw.rs b/crate/atags/src/raw.rs new file mode 100644 index 00000000..abfd1669 --- /dev/null +++ b/crate/atags/src/raw.rs @@ -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 +} diff --git a/crate/bcm2837/Cargo.toml b/crate/bcm2837/Cargo.toml new file mode 100644 index 00000000..3fb3e2e9 --- /dev/null +++ b/crate/bcm2837/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "bcm2837" +version = "0.1.0" +authors = ["equation314 "] + +[features] +use_generic_timer = [] + +[dependencies] +volatile = "0.2.4" +cortex-a = "2.2.2" diff --git a/crate/bcm2837/src/gpio.rs b/crate/bcm2837/src/gpio.rs new file mode 100644 index 00000000..1fb7fecb --- /dev/null +++ b/crate/bcm2837/src/gpio.rs @@ -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; 6], + __reserved0: u32, + SET: [WriteOnly; 2], + __reserved1: u32, + CLR: [WriteOnly; 2], + __reserved2: u32, + LEV: [ReadOnly; 2], + __reserved3: u32, + EDS: [Volatile; 2], + __reserved4: u32, + REN: [Volatile; 2], + __reserved5: u32, + FEN: [Volatile; 2], + __reserved6: u32, + HEN: [Volatile; 2], + __reserved7: u32, + LEN: [Volatile; 2], + __reserved8: u32, + AREN: [Volatile; 2], + __reserved9: u32, + AFEN: [Volatile; 2], + __reserved10: u32, + PUD: Volatile, + PUDCLK: [Volatile; 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 { + pin: u8, + registers: &'static mut Registers, + _state: PhantomData, +} + +impl Gpio { + /// 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(self) -> Gpio { + 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 { + /// Returns a new `GPIO` structure for pin number `pin`. + /// + /// # Panics + /// + /// Panics if `pin` > `53`. + pub fn new(pin: u8) -> Gpio { + 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 { + 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 { + 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 { + self.into_alt(Function::Input).transition() + } +} + +impl Gpio { + /// 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 { + /// 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 + } +} diff --git a/crate/bcm2837/src/interrupt.rs b/crate/bcm2837/src/interrupt.rs new file mode 100644 index 00000000..7324085a --- /dev/null +++ b/crate/bcm2837/src/interrupt.rs @@ -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, + IRQPending: [ReadOnly; 2], + FIQControl: Volatile, + EnableIRQ: [Volatile; 2], + EnableBasicIRQ: Volatile, + DisableIRQ: [Volatile; 2], + DisableBasicIRQ: Volatile, +} + +/// 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 + } +} diff --git a/crate/bcm2837/src/lib.rs b/crate/bcm2837/src/lib.rs new file mode 100644 index 00000000..9635fdce --- /dev/null +++ b/crate/bcm2837/src/lib.rs @@ -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; diff --git a/crate/bcm2837/src/mini_uart.rs b/crate/bcm2837/src/mini_uart.rs new file mode 100644 index 00000000..28e2c13f --- /dev/null +++ b/crate/bcm2837/src/mini_uart.rs @@ -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 = (IO_BASE + 0x215004) as *mut Volatile; + +/// 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, + __r0: [u8; 3], + AUX_MU_IER_REG: Volatile, + __r1: [u8; 3], + AUX_MU_IIR_REG: Volatile, + __r2: [u8; 3], + AUX_MU_LCR_REG: Volatile, + __r3: [u8; 3], + AUX_MU_MCR_REG: Volatile, + __r4: [u8; 3], + AUX_MU_LSR_REG: ReadOnly, + __r5: [u8; 3], + AUX_MU_MSR_REG: ReadOnly, + __r6: [u8; 3], + AUX_MU_SCRATCH: Volatile, + __r7: [u8; 3], + AUX_MU_CNTL_REG: Volatile, + __r8: [u8; 3], + AUX_MU_STAT_REG: ReadOnly, + AUX_MU_BAUD_REG: Volatile, +} + +/// The Raspberry Pi's "mini UART". +pub struct MiniUart { + registers: &'static mut Registers, + timeout: Option, +} + +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() + } +} diff --git a/crate/bcm2837/src/timer/generic_timer.rs b/crate/bcm2837/src/timer/generic_timer.rs new file mode 100644 index 00000000..6c3c0c8a --- /dev/null +++ b/crate/bcm2837/src/timer/generic_timer.rs @@ -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, + _unused1: [Volatile; 8], + LOCAL_IRQ: Volatile, + _unused2: [Volatile; 3], + LOCAL_TIMER_CTL: Volatile, + LOCAL_TIMER_FLAGS: Volatile, + _unused3: Volatile, + CORE_TIMER_IRQCNTL: [Volatile; 4], + CORE_MAILBOX_IRQCNTL: [Volatile; 4], + CORE_IRQ_SRC: [Volatile; 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 + } +} diff --git a/crate/bcm2837/src/timer/mod.rs b/crate/bcm2837/src/timer/mod.rs new file mode 100644 index 00000000..f88b33d1 --- /dev/null +++ b/crate/bcm2837/src/timer/mod.rs @@ -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") } + } +} diff --git a/crate/bcm2837/src/timer/system_timer.rs b/crate/bcm2837/src/timer/system_timer.rs new file mode 100644 index 00000000..cad2af46 --- /dev/null +++ b/crate/bcm2837/src/timer/system_timer.rs @@ -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, + CLO: ReadOnly, + CHI: ReadOnly, + COMPARE: [Volatile; 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) + } +} diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index c60e52ff..4a525d84 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -16,6 +16,10 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "atags" +version = "0.1.0" + [[package]] name = "bare-metal" version = "0.2.4" @@ -28,6 +32,14 @@ dependencies = [ name = "bbl" version = "0.1.0" +[[package]] +name = "bcm2837" +version = "0.1.0" +dependencies = [ + "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bit-allocator" version = "0.1.0" @@ -72,6 +84,14 @@ name = "cfg-if" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cortex-a" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fixedvec" version = "0.2.3" @@ -179,6 +199,14 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "register" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "remove_dir_all" version = "0.5.1" @@ -256,6 +284,11 @@ dependencies = [ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tock-registers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "uart_16550" version = "0.1.0" @@ -270,12 +303,15 @@ name = "ucore" version = "0.1.0" dependencies = [ "apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)", + "atags 0.1.0", "bbl 0.1.0", + "bcm2837 0.1.0", "bit-allocator 0.1.0", "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)", "bootloader 0.3.4 (git+https://github.com/wangrunji0408/bootloader)", "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -288,7 +324,7 @@ dependencies = [ "uart_16550 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ucore-memory 0.1.0", "ucore-process 0.1.0", - "volatile 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -325,7 +361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "volatile" -version = "0.1.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -404,6 +440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bootloader 0.3.4 (git+https://github.com/wangrunji0408/bootloader)" = "" "checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b187d0d728b4a99ba1d79f9671b976bcdd71a8a2c719585218fd2dc14a4d08c" "checksum fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6c16d316ccdac21a4dd648e314e76facbbaf316e83ca137d0857a9c07419d0" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" @@ -419,6 +456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" "checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" +"checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum riscv 0.3.0 (git+https://github.com/riscv-and-rust-and-decaf/riscv)" = "" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" @@ -429,11 +467,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" "checksum static_assertions 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "389ce475f424f267dbed6479cbd8f126c5e1afb053b0acdaa019c74305fc65d1" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" "checksum uart_16550 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "269f953d8de3226f7c065c589c7b4a3e83d10a419c7c3b5e2e0f197e6acc966e" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" "checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41" -"checksum volatile 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37c5d76c0f40ba4f8ac10ec4717d4e98ce3e58c5607eea36e9464226fc5e0a95" +"checksum volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "54d4343a2df2d65144a874f95950754ee7b7e8594f6027aae8c7d0f4858a3fe8" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index e9ee6b34..ba57b56b 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [features] link_user_program = [] no_bbl = [] +board_raspi3 = [] [profile.dev] # MUST >= 1 : Enable RVO to avoid stack overflow @@ -22,7 +23,7 @@ once = "0.3" xmas-elf = "0.6" bitflags = "1.0" bit_field = "0.9" -volatile = "0.1" +volatile = "0.2" linked_list_allocator = "0.6" lazy_static = { version = "1.2", features = ["spin_no_std"] } bit-allocator = { path = "../crate/bit-allocator" } @@ -44,6 +45,11 @@ pc-keyboard = "0.3" riscv = { git = "https://github.com/riscv-and-rust-and-decaf/riscv" } bbl = { path = "../crate/bbl" } +[target.'cfg(target_arch = "aarch64")'.dependencies] +cortex-a = "2.2.1" +atags = { path = "../crate/atags" } +bcm2837 = { path = "../crate/bcm2837", features = ["use_generic_timer"] } + [package.metadata.bootimage] default-target = "x86_64-blog_os.json" output = "target/x86_64-blog_os/bootimage.bin" diff --git a/kernel/Makefile b/kernel/Makefile index 471585c4..6a26d8c6 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -8,14 +8,17 @@ # make clean Clean # # Options: -# arch = x86_64 | riscv32 +# arch = x86_64 | riscv32 | aarch64 # d = int | in_asm | ... QEMU debug info # mode = debug | release # LOG = off | error | warn | info | debug | trace # smp SMP core number -# board Only available on riscv32, build without bbl, run on board +# 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+ arch ?= riscv32 +board ?= raspi3 +prefix ?= $(arch)-none-elf mode ?= debug LOG ?= debug smp ?= 4 @@ -29,25 +32,40 @@ user_bin_path := ../user/target/$(arch)-ucore/debug user_bins := $(patsubst $(user_bin_path)/%.d, $(user_bin_path)/%, $(wildcard $(user_bin_path)/*.d)) user_obj := build/$(arch)/user.o SFSIMG := ../user/ucore32.img + +### qemu options ### ifeq ($(arch), x86_64) qemu_opts := \ -drive format=raw,file=$(bootimage) \ -drive format=raw,file=$(SFSIMG),media=disk,cache=writeback \ -smp cores=$(smp) \ -serial mon:stdio \ - -device isa-debug-exit -endif -ifeq ($(arch), riscv32) + -device isa-debug-exit \ + -nographic +else ifeq ($(arch), riscv32) qemu_opts := \ -machine virt \ -kernel $(bin) \ -nographic \ -smp cores=$(smp) +else ifeq ($(arch), aarch64) +qemu_opts := \ + -machine $(board) \ + -serial null -serial mon:stdio \ + -nographic \ + -kernel $(bin) endif -ifdef board +ifdef d +qemu_opts := $(qemu_opts) -d $(d) +endif + +### build args ### +ifeq ($(arch), riscv32) +ifeq ($(board), fpga) features := $(features) no_bbl endif +endif # Link user binaries at ../user ifdef link_user @@ -55,37 +73,26 @@ features := $(features) link_user_program assembly_object_files := $(assembly_object_files) $(user_obj) endif -ifdef d -qemu_opts := $(qemu_opts) -d $(d) -endif - +features := $(features) board_$(board) build_args := --target $(target).json --features "$(features)" ifeq ($(mode), release) build_args := $(build_args) --release endif -ifeq ($(OS),Windows_NT) -uname := Win32 -else -uname := $(shell uname) -endif -ifeq ($(uname), Darwin) -prefix := x86_64-elf- -endif -ifeq ($(arch), riscv32) -prefix := riscv64-unknown-elf- -endif -ld := $(prefix)ld -objdump := $(prefix)objdump -cc := $(prefix)gcc -as := $(prefix)as +### prefix ### +ld := $(prefix)-ld +objdump := $(prefix)-objdump +objcopy := $(prefix)-objcopy +cc := $(prefix)-gcc +as := $(prefix)-as +gdb := $(prefix)-gdb .PHONY: all clean run build asm doc justrun kernel -all: $(kernel) +all: kernel clean: @cargo clean @@ -99,8 +106,10 @@ run: build justrun justrun: @qemu-system-$(arch) $(qemu_opts) || [ $$? -eq 11 ] # run qemu and assert it exit 11 -debug: $(bin) +debug: $(kernel) $(bin) @qemu-system-$(arch) $(qemu_opts) -s -S & + @sleep 1 + @$(gdb) $(kernel) -x ../tools/gdbinit ifeq ($(arch), x86_64) build: kernel @@ -118,20 +127,24 @@ sym: @$(objdump) -t $(kernel) | less $(bin): kernel -ifdef board +ifeq ($(arch), riscv32) +ifeq ($(board), fpga) @cp $(kernel) $@ else @cd ../riscv-pk && \ - mkdir -p build && \ - cd build && \ - ../configure \ - --enable-32bit \ - --enable-logo \ - --disable-fp-emulation \ - --host=riscv64-unknown-elf \ - --with-payload=$(abspath $(kernel)) && \ - make && \ - cp bbl ../../kernel/$@ + mkdir -p build && \ + cd build && \ + ../configure \ + --enable-32bit \ + --enable-logo \ + --disable-fp-emulation \ + --host=riscv64-unknown-elf \ + --with-payload=$(abspath $(kernel)) && \ + make && \ + cp bbl ../../kernel/$@ +endif +else ifeq ($(arch), aarch64) + $(objcopy) $(kernel) --strip-all -O binary $@ endif kernel: @@ -148,3 +161,24 @@ endif $(user_obj): $(user_bins) @cd $(user_bin_path) && \ $(ld) -o $(abspath $@) $(patsubst %, -b binary %, $(notdir $(user_bins))) + + + +### install ### +ifeq ($(board), raspi3) +sd_card ?= + +ifeq ($(shell uname), Darwin) +sd_card := /Volumes/boot +else ifeq ($(shell uname), Linux) +sd_card := /media/$(shell whoami)/boot +endif + +ifdef sd_card +.PHONY: +install: $(bin) + cp $(bin) $(sd_card)/kernel8.img + sudo umount $(sd_card) +endif + +endif diff --git a/kernel/aarch64-blog_os.json b/kernel/aarch64-blog_os.json new file mode 100644 index 00000000..b0c24dc8 --- /dev/null +++ b/kernel/aarch64-blog_os.json @@ -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 +} diff --git a/kernel/riscv32-blog_os.json b/kernel/riscv32-blog_os.json index 33e9156e..ca35687d 100644 --- a/kernel/riscv32-blog_os.json +++ b/kernel/riscv32-blog_os.json @@ -32,4 +32,4 @@ "msp430-interrupt", "x86-interrupt" ] -} \ No newline at end of file +} diff --git a/kernel/src/arch/aarch64/board/raspi3/irq.rs b/kernel/src/arch/aarch64/board/raspi3/irq.rs new file mode 100644 index 00000000..e01a2c30 --- /dev/null +++ b/kernel/src/arch/aarch64/board/raspi3/irq.rs @@ -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(); + } +} diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs new file mode 100644 index 00000000..2b32a108 --- /dev/null +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -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!"); +} diff --git a/kernel/src/arch/aarch64/board/raspi3/serial.rs b/kernel/src/arch/aarch64/board/raspi3/serial.rs new file mode 100644 index 00000000..5434071f --- /dev/null +++ b/kernel/src/arch/aarch64/board/raspi3/serial.rs @@ -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, +} + +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 = Mutex::new(SerialPort::new()); +pub static mut SERIAL_PORT: SerialPort = SerialPort::new(); + diff --git a/kernel/src/arch/aarch64/board/raspi3/timer.rs b/kernel/src/arch/aarch64/board/raspi3/timer.rs new file mode 100644 index 00000000..4cc0be0d --- /dev/null +++ b/kernel/src/arch/aarch64/board/raspi3/timer.rs @@ -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); +} diff --git a/kernel/src/arch/aarch64/boot/boot.S b/kernel/src/arch/aarch64/boot/boot.S new file mode 100644 index 00000000..b8a142e9 --- /dev/null +++ b/kernel/src/arch/aarch64/boot/boot.S @@ -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 diff --git a/kernel/src/arch/aarch64/boot/linker.ld b/kernel/src/arch/aarch64/boot/linker.ld new file mode 100644 index 00000000..ce4c0c2a --- /dev/null +++ b/kernel/src/arch/aarch64/boot/linker.ld @@ -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*) } +} diff --git a/kernel/src/arch/aarch64/consts.rs b/kernel/src/arch/aarch64/consts.rs new file mode 100644 index 00000000..82d472bd --- /dev/null +++ b/kernel/src/arch/aarch64/consts.rs @@ -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; \ No newline at end of file diff --git a/kernel/src/arch/aarch64/interrupt/context.rs b/kernel/src/arch/aarch64/interrupt/context.rs new file mode 100644 index 00000000..df2b5b34 --- /dev/null +++ b/kernel/src/arch/aarch64/interrupt/context.rs @@ -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) + } +} diff --git a/kernel/src/arch/aarch64/interrupt/handler.rs b/kernel/src/arch/aarch64/interrupt/handler.rs new file mode 100644 index 00000000..85d1faf7 --- /dev/null +++ b/kernel/src/arch/aarch64/interrupt/handler.rs @@ -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; +} diff --git a/kernel/src/arch/aarch64/interrupt/mod.rs b/kernel/src/arch/aarch64/interrupt/mod.rs new file mode 100644 index 00000000..140e9b85 --- /dev/null +++ b/kernel/src/arch/aarch64/interrupt/mod.rs @@ -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); +} diff --git a/kernel/src/arch/aarch64/interrupt/syndrome.rs b/kernel/src/arch/aarch64/interrupt/syndrome.rs new file mode 100644 index 00000000..d466a5d2 --- /dev/null +++ b/kernel/src/arch/aarch64/interrupt/syndrome.rs @@ -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 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 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), + } + } +} diff --git a/kernel/src/arch/aarch64/interrupt/trap.S b/kernel/src/arch/aarch64/interrupt/trap.S new file mode 100644 index 00000000..331ba41a --- /dev/null +++ b/kernel/src/arch/aarch64/interrupt/trap.S @@ -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 diff --git a/kernel/src/arch/aarch64/interrupt/vector.S b/kernel/src/arch/aarch64/interrupt/vector.S new file mode 100644 index 00000000..3a749f94 --- /dev/null +++ b/kernel/src/arch/aarch64/interrupt/vector.S @@ -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 diff --git a/kernel/src/arch/aarch64/io.rs b/kernel/src/arch/aarch64/io.rs new file mode 100644 index 00000000..deda39c3 --- /dev/null +++ b/kernel/src/arch/aarch64/io.rs @@ -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() + } +} diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs new file mode 100644 index 00000000..bf553546 --- /dev/null +++ b/kernel/src/arch/aarch64/memory.rs @@ -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 +} diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs new file mode 100644 index 00000000..58aa7846 --- /dev/null +++ b/kernel/src/arch/aarch64/mod.rs @@ -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")); diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs new file mode 100644 index 00000000..e9cc43e8 --- /dev/null +++ b/kernel/src/arch/aarch64/paging.rs @@ -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::(); + 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 { + alloc_frame() + } + + fn dealloc_frame(target: PhysAddr) { + dealloc_frame(target) + } + + fn alloc_stack() -> Stack { + alloc_stack() + } +} diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 76b3b961..fb3a6a86 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -156,4 +156,4 @@ impl INodeExt for INode { self.read_at(0, buf.as_mut_slice())?; Ok(buf) } -} \ No newline at end of file +} diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index cc3b5430..043883e3 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -42,6 +42,10 @@ pub mod arch; #[path = "arch/riscv32/mod.rs"] pub mod arch; +#[cfg(target_arch = "aarch64")] +#[path = "arch/aarch64/mod.rs"] +pub mod arch; + pub fn kmain() -> ! { processor().run(); diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 3d1f463e..8ebca5f4 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -23,6 +23,10 @@ pub type FrameAlloc = BitAlloc64K; #[cfg(target_arch = "riscv32")] pub type FrameAlloc = BitAlloc4K; +// Raspberry Pi 3 has 1G memory +#[cfg(target_arch = "aarch64")] +pub type FrameAlloc = BitAlloc64K; + lazy_static! { pub static ref FRAME_ALLOCATOR: SpinNoIrqLock = SpinNoIrqLock::new(FrameAlloc::default()); } diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 2039bd3b..c377f638 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -51,6 +51,15 @@ impl ContextImpl { }) } + pub fn new_user_test(entry: extern fn(usize) -> !) -> Self { + let ms = MemorySet::new(); + let user_stack = ::memory::alloc_stack(); + Context { + arch: unsafe { ArchContext::new_user_thread(entry as usize, user_stack.top - 8, ms.kstack_top(), false, ms.token()) }, + memory_set: ms, + } + } + /// Make a new user thread from ELF data /* * @param: diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index b26144c9..1d2147d9 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -53,4 +53,4 @@ pub fn processor() -> &'static Processor { #[no_mangle] pub fn new_kernel_context(entry: extern fn(usize) -> !, arg: usize) -> Box { ContextImpl::new_kernel(entry, arg) -} \ No newline at end of file +} diff --git a/kernel/src/sync/mutex.rs b/kernel/src/sync/mutex.rs index 92ac5582..4157ec3b 100644 --- a/kernel/src/sync/mutex.rs +++ b/kernel/src/sync/mutex.rs @@ -217,6 +217,8 @@ impl MutexSupport for Spin { asm!("pause" :::: "volatile"); #[cfg(target_arch = "riscv32")] asm!("nop" :::: "volatile"); + #[cfg(target_arch = "aarch64")] + asm!("yield" :::: "volatile"); } } fn before_lock() -> Self::GuardData {} @@ -247,6 +249,8 @@ impl MutexSupport for SpinNoIrq { asm!("pause" :::: "volatile"); #[cfg(target_arch = "riscv32")] asm!("nop" :::: "volatile"); + #[cfg(target_arch = "aarch64")] + asm!("yield" :::: "volatile"); } } fn before_lock() -> Self::GuardData { @@ -267,4 +271,4 @@ impl MutexSupport for Condvar { fn after_unlock(&self) { self.notify_one(); } -} \ No newline at end of file +} diff --git a/tools/gdbinit b/tools/gdbinit new file mode 100644 index 00000000..5041b6e7 --- /dev/null +++ b/tools/gdbinit @@ -0,0 +1,3 @@ +target remote :1234 +break rust_main +continue diff --git a/user/aarch64-ucore.json b/user/aarch64-ucore.json new file mode 100644 index 00000000..357c2e3c --- /dev/null +++ b/user/aarch64-ucore.json @@ -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 +} diff --git a/user/ucore-ulib/src/syscall.rs b/user/ucore-ulib/src/syscall.rs index 5d8765b1..f7071429 100644 --- a/user/ucore-ulib/src/syscall.rs +++ b/user/ucore-ulib/src/syscall.rs @@ -44,6 +44,12 @@ fn sys_call(id: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: : "{rax}" (id), "{rdi}" (arg0), "{rsi}" (arg1), "{rdx}" (arg2), "{rcx}" (arg3), "{r8}" (arg4), "{r9}" (arg5) : "memory" : "intel" "volatile"); + #[cfg(target_arch = "aarch64")] + asm!("svc 0" + : "={x0}" (ret) + : "{x8}" (id), "{x0}" (arg0), "{x1}" (arg1), "{x2}" (arg2), "{x3}" (arg3), "{x4}" (arg4), "{x5}" (arg5) + : "memory" + : "volatile"); } ret } @@ -68,7 +74,7 @@ pub fn sys_open(path: &str, flags: usize) -> i32 { } pub fn sys_close(fd: usize) -> i32 { - sys_call(SYS_CLOSE, fd, 0 , 0, 0, 0, 0) + sys_call(SYS_CLOSE, fd, 0, 0, 0, 0, 0) } /// Fork the current process. Return the child's PID. @@ -137,4 +143,4 @@ const SYS_FSYNC: usize = 111; const SYS_GETCWD: usize = 121; const SYS_GETDIRENTRY: usize = 128; const SYS_DUP: usize = 130; -const SYS_LAB6_SET_PRIORITY: usize = 255; \ No newline at end of file +const SYS_LAB6_SET_PRIORITY: usize = 255;