├── .gitignore ├── src ├── memory │ ├── mod.rs │ ├── virtual_mem │ │ ├── mod.rs │ │ └── address.rs │ ├── atags │ │ ├── mod.rs │ │ ├── raw.rs │ │ └── atag.rs │ └── allocator │ │ └── mod.rs ├── arch │ ├── aarch64 │ │ ├── mod.rs │ │ ├── context │ │ │ ├── mod.rs │ │ │ ├── stack.rs │ │ │ ├── process.rs │ │ │ └── scheduler.rs │ │ ├── registers │ │ │ ├── cntvct_el0.rs │ │ │ ├── current_el.rs │ │ │ ├── mod.rs │ │ │ ├── cntfrq_el0.rs │ │ │ ├── cntv_tval_el0.rs │ │ │ └── cntv_ctl_el0.rs │ │ └── exceptions │ │ │ ├── irq.rs │ │ │ ├── trap_frame.rs │ │ │ ├── mod.rs │ │ │ ├── interrupt.rs │ │ │ └── esr.rs │ └── mod.rs ├── io │ ├── mod.rs │ ├── generic_timer.rs │ ├── mini_uart.rs │ ├── console.rs │ └── gpio.rs ├── lib.rs ├── main.rs └── asm │ └── init.S ├── Cargo.toml ├── aarch64-none-elf.json ├── layout.ld ├── README.md ├── LICENSE.md ├── Makefile └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | target/ 3 | **/*.swp 4 | -------------------------------------------------------------------------------- /src/memory/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod atags; 2 | 3 | pub mod allocator; 4 | 5 | pub mod virtual_mem; 6 | -------------------------------------------------------------------------------- /src/arch/aarch64/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod registers; 2 | 3 | pub mod exceptions; 4 | 5 | pub mod context; 6 | -------------------------------------------------------------------------------- /src/memory/virtual_mem/mod.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | 3 | pub use self::address::{PhysicalAddr, VirtualAddr}; 4 | -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | //#[cfg(target_arch = "aarch64")] 2 | #[macro_use] 3 | pub mod aarch64; 4 | //#[cfg(target_arch = "aarch64")] 5 | pub use self::aarch64::*; 6 | -------------------------------------------------------------------------------- /src/arch/aarch64/context/mod.rs: -------------------------------------------------------------------------------- 1 | mod stack; 2 | mod process; 3 | mod scheduler; 4 | 5 | pub use self::scheduler::{GlobalScheduler, TICK}; 6 | pub use self::process::{State}; 7 | -------------------------------------------------------------------------------- /src/io/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod console; 3 | 4 | pub mod gpio; 5 | 6 | pub mod mini_uart; 7 | 8 | //pub mod timer; 9 | 10 | pub mod generic_timer; 11 | 12 | /// Base address of IO devices 13 | pub const IO_BASE: usize = 0x3F000000; 14 | 15 | -------------------------------------------------------------------------------- /src/arch/aarch64/registers/cntvct_el0.rs: -------------------------------------------------------------------------------- 1 | pub use register::cpu::RegisterReadOnly; 2 | 3 | pub struct Reg; 4 | 5 | impl RegisterReadOnly for Reg { 6 | #[inline] 7 | fn get(&self) -> u64 { 8 | let reg: u64; 9 | unsafe { 10 | asm!("mrs $0, CNTVCT_EL0" : "=r"(reg) ::: "volatile"); 11 | } 12 | reg 13 | } 14 | } 15 | 16 | pub static CNTVCT_EL0: Reg = Reg {}; 17 | -------------------------------------------------------------------------------- /src/arch/aarch64/registers/current_el.rs: -------------------------------------------------------------------------------- 1 | pub use register::cpu::RegisterReadOnly; 2 | 3 | pub struct Reg; 4 | 5 | impl RegisterReadOnly for Reg { 6 | #[inline] 7 | fn get(&self) -> u32 { 8 | let reg: u32; 9 | unsafe { 10 | asm!("mrs $0, CurrentEL" : "=r"(reg) ::: "volatile"); 11 | } 12 | reg >> 2 13 | } 14 | } 15 | 16 | pub static CURRENT_EL: Reg = Reg {}; 17 | -------------------------------------------------------------------------------- /src/arch/aarch64/registers/mod.rs: -------------------------------------------------------------------------------- 1 | mod current_el; 2 | mod cntfrq_el0; 3 | mod cntv_ctl_el0; 4 | mod cntv_tval_el0; 5 | mod cntvct_el0; 6 | 7 | pub use register::cpu::RegisterReadWrite; 8 | pub use register::cpu::RegisterReadOnly; 9 | 10 | pub use self::current_el::CURRENT_EL; 11 | pub use self::cntfrq_el0::CNTFRQ_EL0; 12 | pub use self::cntv_ctl_el0::CNTV_CTL_EL0; 13 | pub use self::cntv_tval_el0::CNTV_TVAL_EL0; 14 | pub use self::cntvct_el0::CNTVCT_EL0; 15 | 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustberry" 3 | version = "0.1.0" 4 | authors = ["Patrik Cyvoct "] 5 | 6 | [profile.dev] 7 | panic = "abort" 8 | debug = true 9 | 10 | [profile.test] 11 | debug = true 12 | 13 | [profile.release] 14 | panic = "abort" 15 | 16 | [dependencies] 17 | volatile = "0.2.3" 18 | spin = "0.4.6" 19 | once = "0.3.2" 20 | linked_list_allocator = "0.6.3" 21 | register = "0.1.1" 22 | 23 | [dependencies.lazy_static] 24 | version = "1.0" 25 | features = ["spin_no_std"] 26 | -------------------------------------------------------------------------------- /src/arch/aarch64/exceptions/irq.rs: -------------------------------------------------------------------------------- 1 | use super::interrupt::Interrupt; 2 | use super::trap_frame::TrapFrame; 3 | use arch::context::State; 4 | use io::generic_timer; 5 | use SCHEDULER; 6 | pub fn handle_irq(interrupt: Interrupt, tf: &mut TrapFrame) { 7 | // kprintln!("{:?}", interrupt); 8 | //generic_timer::tick_in(100); 9 | generic_timer::ack(); 10 | unsafe { 11 | let reg: u32; 12 | asm!("mrs $0, CNTPCT_EL0" : "=r"(reg) ::: "volatile"); 13 | kprintln!("{}", reg); 14 | } 15 | SCHEDULER.switch(State::Ready, &mut *tf); 16 | } 17 | -------------------------------------------------------------------------------- /src/arch/aarch64/registers/cntfrq_el0.rs: -------------------------------------------------------------------------------- 1 | pub use register::cpu::RegisterReadWrite; 2 | 3 | pub struct Reg; 4 | 5 | impl RegisterReadWrite for Reg { 6 | #[inline] 7 | fn get(&self) -> u32 { 8 | let reg: u32; 9 | unsafe { 10 | asm!("mrs $0, CNTFRQ_EL0" : "=r"(reg) ::: "volatile"); 11 | } 12 | reg 13 | } 14 | 15 | #[inline] 16 | fn set(&self, value: u32) { 17 | unsafe { 18 | asm!("msr CNTFRQ_EL0, $0" :: "r"(value) :: "volatile"); 19 | } 20 | } 21 | } 22 | 23 | pub static CNTFRQ_EL0: Reg = Reg {}; 24 | -------------------------------------------------------------------------------- /src/arch/aarch64/registers/cntv_tval_el0.rs: -------------------------------------------------------------------------------- 1 | pub use register::cpu::RegisterReadWrite; 2 | 3 | pub struct Reg; 4 | 5 | impl RegisterReadWrite for Reg { 6 | #[inline] 7 | fn get(&self) -> u32 { 8 | let reg: u32; 9 | unsafe { 10 | asm!("mrs $0, CNTV_TVAL_EL0" : "=r"(reg) ::: "volatile"); 11 | } 12 | reg 13 | } 14 | 15 | #[inline] 16 | fn set(&self, value: u32) { 17 | unsafe { 18 | asm!("msr CNTV_TVAL_EL0, $0" :: "r"(value) :: "volatile"); 19 | } 20 | } 21 | } 22 | 23 | pub static CNTV_TVAL_EL0: Reg = Reg {}; 24 | -------------------------------------------------------------------------------- /src/memory/atags/mod.rs: -------------------------------------------------------------------------------- 1 | // Thanks to https://web.stanford.edu/class/cs140e (a lot of the code is from there) 2 | 3 | mod raw; 4 | 5 | mod atag; 6 | 7 | pub use self::atag::*; 8 | 9 | /// Address of ATAGs 10 | const ATAG_BASE: usize = 0x100; 11 | 12 | pub struct Atags { 13 | ptr: &'static raw::RawAtag, 14 | } 15 | 16 | impl Atags { 17 | pub fn get() -> Atags { 18 | Atags { 19 | ptr: unsafe { &*(ATAG_BASE as *const raw::RawAtag) } 20 | } 21 | } 22 | } 23 | 24 | impl Iterator for Atags { 25 | type Item = Atag; 26 | 27 | fn next(&mut self) -> Option { 28 | let atag = self.ptr; 29 | self.ptr = self.ptr.next()?; 30 | Some(Atag::from(atag)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/arch/aarch64/registers/cntv_ctl_el0.rs: -------------------------------------------------------------------------------- 1 | pub use register::cpu::RegisterReadWrite; 2 | 3 | register_bitfields! {u32, 4 | CNTV_CTL_EL0 [ 5 | ENABLE OFFSET(0) NUMBITS(1) [], 6 | IMASK OFFSET(1) NUMBITS(1) [], 7 | ISTATUS OFFSET(2) NUMBITS(1) [] 8 | ] 9 | } 10 | 11 | pub struct Reg; 12 | 13 | impl RegisterReadWrite for Reg { 14 | #[inline] 15 | fn get(&self) -> u32 { 16 | let reg: u32; 17 | unsafe { 18 | asm!("mrs $0, CNTV_CTL_EL0" : "=r"(reg) ::: "volatile"); 19 | } 20 | reg 21 | } 22 | 23 | #[inline] 24 | fn set(&self, value: u32) { 25 | unsafe { 26 | asm!("msr CNTV_CTL_EL0, $0" :: "r"(value) :: "volatile"); 27 | } 28 | } 29 | } 30 | 31 | pub static CNTV_CTL_EL0: Reg = Reg {}; 32 | -------------------------------------------------------------------------------- /src/memory/virtual_mem/address.rs: -------------------------------------------------------------------------------- 1 | pub struct VirtualAddr(usize); 2 | 3 | pub struct PhysicalAddr(usize); 4 | 5 | impl From<*mut T> for PhysicalAddr { 6 | fn from(ptr: *mut T) -> PhysicalAddr { 7 | PhysicalAddr(ptr as usize) 8 | } 9 | } 10 | 11 | impl PhysicalAddr { 12 | pub fn as_ptr(&self) -> *const u8 { 13 | self.0 as *const u8 14 | } 15 | 16 | pub fn as_mut_ptr(&mut self) -> *mut u8 { 17 | self.0 as *mut u8 18 | } 19 | 20 | pub fn as_usize(&self) -> usize { 21 | self.0 22 | } 23 | 24 | #[cfg(target_pointer_width = "64")] 25 | pub fn as_u64(&self) -> u64 { 26 | self.0 as u64 27 | } 28 | } 29 | use core::fmt; 30 | impl fmt::Debug for PhysicalAddr { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | write!(f, "{}({:#x})", stringify!(PhysicalAddr), self.0) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /aarch64-none-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi-blacklist": [ 3 | "stdcall", 4 | "fastcall", 5 | "vectorcall", 6 | "thiscall", 7 | "win64", 8 | "sysv64" 9 | ], 10 | "arch": "aarch64", 11 | "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", 12 | "executables": true, 13 | "linker-flavor": "ld.lld", 14 | "linker": "rust-lld", 15 | "linker-is-gnu": true, 16 | "pre-link-args": { 17 | "ld.lld": [ 18 | "--script=layout.ld" 19 | ] 20 | }, 21 | "llvm-target": "aarch64-unknown-none", 22 | "no-compiler-rt": true, 23 | "features": "+a53,+strict-align", 24 | "max-atomic-width": 128, 25 | "os": "none", 26 | "panic": "abort", 27 | "panic-strategy": "abort", 28 | "relocation-model": "pic", 29 | "target-c-int-width": "32", 30 | "target-endian": "little", 31 | "target-pointer-width": "64", 32 | "disable-redzone": true 33 | } 34 | -------------------------------------------------------------------------------- /layout.ld: -------------------------------------------------------------------------------- 1 | SECTIONS { 2 | . = 0x80000; /* Raspbery Pi 3 Aarch64 (kernel8.img) load address */ 3 | 4 | /* start of the binary */ 5 | _start = .; 6 | 7 | .text : { 8 | KEEP(*(.text.init)) /* from init.S */ 9 | *(.text .text.* .gnu.linkonce.t*) 10 | } 11 | 12 | .rodata : { 13 | *(.rodata .rodata.* .gnu.linkonce.r*) 14 | } 15 | 16 | .data : { 17 | *(.data .data.* .gnu.linkonce.d*) 18 | } 19 | 20 | .bss (NOLOAD) : { 21 | . = ALIGN(32); 22 | __bss_start = .; 23 | *(.bss .bss.*) 24 | *(COMMON) 25 | . = ALIGN(8); 26 | __bss_end = .; 27 | } 28 | 29 | /* end of the binary */ 30 | _end = ALIGN(8); 31 | 32 | /* number of bytes in BSS section and complete binary */ 33 | __bss_length = (__bss_end - __bss_start); 34 | __binary_length = (_end - _start); 35 | 36 | /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustberry OS 2 | This is a WIP kernel for RaspberryPi 3 written in Rust. 3 | Some code is taken from this [Stanford class](https://web.stanford.edu/class/cs140e). 4 | 5 | ## Requirements 6 | You will nedd a nightly Rust installation. The fastest way is to use [rustup](https://rustup.rs/). 7 | You will also need some tools, you can install the whole toolchain with: 8 | ```shell 9 | $ curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly 10 | $ rustup component add rust-src llvm-tools-preview 11 | $ cargo install cargo-xbuild cargo-binutils 12 | ``` 13 | 14 | ## Building 15 | * To build a debug kernel: `DEBUG=1 make` 16 | * To build a release kernel: `make` 17 | 18 | ## Running 19 | ### QEMU 20 | To run the kernel in QEMU you can either run (you will need QEMU): 21 | * `make run` 22 | * `qemu-system-aarch64 -kernel build/kernel8.img -M raspi3 -serial null -serial mon:stdio` 23 | 24 | ## Testing (WIP) 25 | `make test` 26 | -------------------------------------------------------------------------------- /src/memory/allocator/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::alloc::Layout; 2 | use HEAP_ALLOCATOR; 3 | pub fn init_heap() { 4 | let (start, end) = memory_map().expect("failed to find memory map"); 5 | unsafe { 6 | HEAP_ALLOCATOR.lock().init(start, end - start); 7 | } 8 | } 9 | 10 | //#[lang = "oom"] 11 | #[no_mangle] 12 | #[alloc_error_handler] 13 | pub fn rust_oom(_layout: Layout) -> ! { 14 | panic!("out of memory"); 15 | } 16 | 17 | extern "C" { 18 | static _end: u8; 19 | } 20 | 21 | use memory::atags::{Atag, Atags}; 22 | 23 | fn memory_map() -> Option<(usize, usize)> { 24 | let binary_end = unsafe { (&_end as *const u8) as u32 }; 25 | let mut size = 0; 26 | for v in Atags::get() { 27 | match v { 28 | Atag::Mem(mem) => { 29 | size = mem.size; 30 | } 31 | _ => {} // not used for now at least 32 | } 33 | } 34 | 35 | Some((binary_end as usize, (size - binary_end) as usize)) 36 | } 37 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items)] 2 | #![feature(panic_implementation)] 3 | #![feature(core_intrinsics)] 4 | #![feature(const_fn)] 5 | #![feature(asm)] 6 | #![feature(optin_builtin_traits)] 7 | #![feature(decl_macro)] 8 | #![feature(attr_literals)] 9 | #![feature(never_type)] 10 | #![feature(ptr_internals)] 11 | #![feature(global_asm)] 12 | #![feature(alloc, allocator_api)] 13 | #![feature(alloc_error_handler)] 14 | 15 | #![no_std] 16 | 17 | extern crate volatile; 18 | extern crate spin; 19 | #[macro_use] 20 | extern crate once; 21 | #[macro_use] 22 | extern crate lazy_static; 23 | extern crate linked_list_allocator; 24 | extern crate alloc; 25 | 26 | #[macro_use] 27 | extern crate register; 28 | 29 | #[macro_use] 30 | pub mod io; 31 | pub mod memory; 32 | pub mod arch; 33 | 34 | use linked_list_allocator::LockedHeap; 35 | 36 | #[cfg(not(test))] 37 | #[global_allocator] 38 | pub static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); 39 | 40 | use arch::context::GlobalScheduler; 41 | pub static SCHEDULER: GlobalScheduler = GlobalScheduler::uninitialized(); 42 | 43 | use io::generic_timer::GenericTimer; 44 | pub static TIMER: Option = None; 45 | 46 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [Patrik Cyvoct ] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items)] 2 | #![feature(panic_implementation)] 3 | #![feature(core_intrinsics)] 4 | #![feature(const_fn)] 5 | #![feature(asm)] 6 | #![feature(optin_builtin_traits)] 7 | #![feature(decl_macro)] 8 | #![feature(attr_literals)] 9 | #![feature(never_type)] 10 | #![feature(ptr_internals)] 11 | #![feature(global_asm)] 12 | #![no_std] 13 | #![cfg_attr(not(test), no_main)] 14 | #![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))] 15 | #![feature(alloc, allocator_api)] 16 | 17 | #[cfg(not(test))] 18 | global_asm!(include_str!("asm/init.S")); 19 | 20 | #[cfg(test)] 21 | extern crate std; 22 | 23 | #[macro_use] 24 | extern crate rustberry; 25 | extern crate alloc; 26 | extern crate volatile; 27 | 28 | #[cfg(not(test))] 29 | #[no_mangle] 30 | pub extern "C" fn main() { 31 | rustberry::memory::allocator::init_heap(); 32 | rustberry::io::console::CONSOLE.lock().init(); 33 | rustberry::SCHEDULER.start(); 34 | } 35 | 36 | #[cfg(not(test))] 37 | #[lang = "eh_personality"] 38 | pub extern "C" fn eh_personality() {} 39 | 40 | use core::panic::PanicInfo; 41 | 42 | #[cfg(not(test))] 43 | #[panic_implementation] 44 | #[no_mangle] 45 | pub extern "C" fn panic(info: &PanicInfo) -> ! { 46 | kprintln!("{}", info); 47 | loop {} 48 | } 49 | -------------------------------------------------------------------------------- /src/memory/atags/raw.rs: -------------------------------------------------------------------------------- 1 | /// A raw ATAG 2 | #[repr(C)] 3 | pub struct RawAtag { 4 | pub dwords: u32, 5 | pub tag: u32, 6 | pub kind: Kind, 7 | } 8 | 9 | impl RawAtag { 10 | pub const NONE: u32 = 0x00000000; 11 | pub const CORE: u32 = 0x54410001; 12 | pub const MEM: u32 = 0x54410002; 13 | pub const VIDEOTEXT: u32 = 0x54410003; 14 | pub const RAMDISK: u32 = 0x54410004; 15 | pub const INITRD2: u32 = 0x54420005; 16 | pub const SERIAL: u32 = 0x54410006; 17 | pub const REVISION: u32 = 0x54410007; 18 | pub const VIDEOLFB: u32 = 0x54410008; 19 | pub const CMDLINE: u32 = 0x54410009; 20 | 21 | /// Returns the next `RawAtag` or `None` 22 | pub fn next(&self) -> Option<&RawAtag> { 23 | if self.tag == RawAtag::NONE { 24 | return None; 25 | } 26 | let atag = unsafe { 27 | (&(self as *const RawAtag as *const u32)).offset(self.dwords as isize) as *const RawAtag 28 | }; 29 | unsafe { Some(&*atag) } 30 | } 31 | } 32 | 33 | /// Different kind of ATAG 34 | #[repr(C)] 35 | pub union Kind { 36 | pub core: Core, 37 | pub mem: Mem, 38 | pub cmd: Cmd, 39 | } 40 | 41 | /// A core ATAG 42 | #[repr(C)] 43 | #[derive(Debug, Copy, Clone)] 44 | pub struct Core { 45 | pub flags: u32, 46 | pub page_size: u32, 47 | pub root_dev: u32, 48 | } 49 | 50 | /// A mem ATAG 51 | #[repr(C)] 52 | #[derive(Debug, Copy, Clone)] 53 | pub struct Mem { 54 | pub size: u32, 55 | pub start: u32, 56 | } 57 | 58 | /// A cmd ATAG 59 | #[repr(C)] 60 | #[derive(Debug, Copy, Clone)] 61 | pub struct Cmd { 62 | pub cmd: u8, 63 | } 64 | -------------------------------------------------------------------------------- /src/io/generic_timer.rs: -------------------------------------------------------------------------------- 1 | use arch::registers::*; 2 | use volatile::*; 3 | 4 | const GEN_TIMER_REG_BASE: usize = 0x40000000; 5 | 6 | #[allow(non_snake_case)] 7 | #[repr(C)] 8 | struct Registers { 9 | CONTROL: Volatile, 10 | _unused1: [Volatile; 8], 11 | LOCAL_IRQ: Volatile, 12 | _unused2: [Volatile; 3], 13 | LOCAL_TIMER_CTL: Volatile, 14 | LOCAL_TIMER_FLAGS: Volatile, 15 | _unused3: Volatile, 16 | CORE_TIMER_IRQCNTL: [Volatile; 4], 17 | CORE_MAILBOX_IRQCNTL: [Volatile; 4], 18 | CORE_IRQ_SRC: [Volatile; 4], 19 | } 20 | 21 | pub struct GenericTimer { 22 | registers: &'static mut Registers, 23 | } 24 | 25 | impl GenericTimer { 26 | pub fn new() -> GenericTimer { 27 | GenericTimer { 28 | registers: unsafe { &mut *(GEN_TIMER_REG_BASE as *mut Registers) }, 29 | } 30 | } 31 | 32 | pub fn ack(&mut self) { 33 | if self.registers.CORE_IRQ_SRC[0].read() & 0x08 != 0 { 34 | CNTV_TVAL_EL0.set(62500000); 35 | } 36 | } 37 | 38 | pub fn tick_in(&mut self, us: u32) { 39 | let cntfrq = CNTFRQ_EL0.get(); 40 | CNTV_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32); 41 | } 42 | 43 | pub fn init(&mut self) { 44 | let cntfrq = CNTFRQ_EL0.get(); 45 | 46 | CNTV_TVAL_EL0.set(cntfrq); 47 | 48 | self.registers.CORE_TIMER_IRQCNTL[0].write(0x08); 49 | CNTV_CTL_EL0.set(1); 50 | } 51 | } 52 | 53 | pub fn ack() { 54 | GenericTimer::new().ack(); 55 | } 56 | 57 | pub fn tick_in(us: u32) { 58 | GenericTimer::new().tick_in(us); 59 | } 60 | -------------------------------------------------------------------------------- /src/arch/aarch64/exceptions/trap_frame.rs: -------------------------------------------------------------------------------- 1 | #[repr(C)] 2 | #[derive(Default, Debug, Copy, Clone)] 3 | pub struct TrapFrame { 4 | pub elr: u64, 5 | pub spsr: u64, 6 | pub sp: u64, 7 | pub tpidr: u64, 8 | pub q0: u128, 9 | pub q1: u128, 10 | pub q2: u128, 11 | pub q3: u128, 12 | pub q4: u128, 13 | pub q5: u128, 14 | pub q6: u128, 15 | pub q7: u128, 16 | pub q8: u128, 17 | pub q9: u128, 18 | pub q10: u128, 19 | pub q11: u128, 20 | pub q12: u128, 21 | pub q13: u128, 22 | pub q14: u128, 23 | pub q15: u128, 24 | pub q16: u128, 25 | pub q17: u128, 26 | pub q18: u128, 27 | pub q19: u128, 28 | pub q20: u128, 29 | pub q21: u128, 30 | pub q22: u128, 31 | pub q23: u128, 32 | pub q24: u128, 33 | pub q25: u128, 34 | pub q26: u128, 35 | pub q27: u128, 36 | pub q28: u128, 37 | pub q29: u128, 38 | pub q30: u128, 39 | pub q31: u128, 40 | pub x1: u64, 41 | pub x2: u64, 42 | pub x3: u64, 43 | pub x4: u64, 44 | pub x5: u64, 45 | pub x6: u64, 46 | pub x7: u64, 47 | pub x8: u64, 48 | pub x9: u64, 49 | pub x10: u64, 50 | pub x11: u64, 51 | pub x12: u64, 52 | pub x13: u64, 53 | pub x14: u64, 54 | pub x15: u64, 55 | pub x16: u64, 56 | pub x17: u64, 57 | pub x18: u64, 58 | pub x19: u64, 59 | pub x20: u64, 60 | pub x21: u64, 61 | pub x22: u64, 62 | pub x23: u64, 63 | pub x24: u64, 64 | pub x25: u64, 65 | pub x26: u64, 66 | pub x27: u64, 67 | pub x28: u64, 68 | pub x29: u64, 69 | pub _reserved: u64, 70 | pub x30: u64, 71 | pub x0: u64, 72 | } 73 | -------------------------------------------------------------------------------- /src/arch/aarch64/exceptions/mod.rs: -------------------------------------------------------------------------------- 1 | mod esr; 2 | mod trap_frame; 3 | mod interrupt; 4 | mod irq; 5 | 6 | pub use self::trap_frame::TrapFrame; 7 | 8 | #[repr(u16)] 9 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 10 | pub enum Kind { 11 | Synchronous = 0, 12 | Irq = 1, 13 | Fiq = 2, 14 | SError = 3, 15 | } 16 | 17 | #[repr(u16)] 18 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 19 | pub enum Source { 20 | CurrentSpEl0 = 0, 21 | CurrentSpElx = 1, 22 | LowerAArch64 = 2, 23 | LowerAArch32 = 3, 24 | } 25 | 26 | #[repr(C)] 27 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 28 | pub struct Info { 29 | source: Source, 30 | kind: Kind, 31 | } 32 | 33 | 34 | #[no_mangle] 35 | pub extern "C" fn handle_exception(info: Info, esr: u32, tf: &mut TrapFrame) { 36 | use self::esr::Syndrome; 37 | 38 | match info.kind { 39 | Kind::Synchronous => { 40 | match Syndrome::from(esr) { 41 | Syndrome::Brk(_) => { 42 | tf.elr += 4; 43 | kprintln!("{:?}", esr::Syndrome::from(esr)); 44 | // kprintln!("{:?}", info); 45 | }, 46 | _ => {}, 47 | } 48 | }, 49 | Kind::Irq => { 50 | use self::irq; 51 | use self::interrupt; 52 | let ctl = interrupt::Controller::new(); 53 | if ctl.is_pending(interrupt::Interrupt::Timer1) { 54 | irq::handle_irq(interrupt::Interrupt::Timer1, &mut *tf); 55 | } else { 56 | kprintln!("IRQ != 1 WIP"); 57 | } 58 | } 59 | _ => { 60 | kprintln!("Other WIP"); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET ?= aarch64-none-elf 2 | 3 | CARGO ?= RUST_TARGET_PATH="$(shell pwd)" cargo 4 | CARGO_BUILD ?= $(CARGO) xbuild 5 | 6 | OBJCOPY = cargo objcopy -- 7 | OBJCOPY_PARAMS = --strip-all -O binary 8 | 9 | RUST_BINARY := $(shell cat Cargo.toml | grep name | cut -d\" -f 2 | tr - _) 10 | RUST_BUILD_DIR := target/$(TARGET) 11 | RUST_DEBUG_LIB := $(RUST_BUILD_DIR)/debug/$(RUST_BINARY) 12 | RUST_RELEASE_LIB := $(RUST_BUILD_DIR)/release/$(RUST_BINARY) 13 | 14 | RUST_DEPS = Cargo.toml src/* 15 | 16 | BUILD_DIR := build 17 | RUST_LIB := $(BUILD_DIR)/$(RUST_BINARY) 18 | KERNEL = $(BUILD_DIR)/kernel8.img 19 | 20 | TEST_KERNEL_SRC = $(basename $(wildcard src/bin/*.rs)) 21 | TEST_KERNEL = $(TEST_KERNEL_SRC:src/bin/%=%) 22 | 23 | .PHONY: all clean test $(RUST_DEBUG_LIB) 24 | 25 | all: $(KERNEL) 26 | 27 | $(RUST_DEBUG_LIB): $(RUST_DEPS) 28 | @echo "+ Building $@" 29 | $(CARGO_BUILD) --target=$(TARGET) 30 | 31 | $(RUST_RELEASE_LIB): $(RUST_DEPS) 32 | @echo "+ Building $@" 33 | $(CARGO_BUILD) --release --target=$(TARGET) 34 | 35 | ifeq ($(DEBUG),1) 36 | $(RUST_LIB): $(RUST_DEBUG_LIB) | $(BUILD_DIR) 37 | @cp $< $@ 38 | else 39 | $(RUST_LIB): $(RUST_RELEASE_LIB) | $(BUILD_DIR) 40 | @cp $< $@ 41 | endif 42 | 43 | $(BUILD_DIR): 44 | @mkdir -p $@ 45 | 46 | 47 | $(KERNEL): $(RUST_DEBUG_LIB) | $(BUILD_DIR) 48 | @echo "+ Building $@" 49 | $(OBJCOPY) $(OBJCOPY_PARAMS) $< $@ 50 | 51 | run: $(KERNEL) 52 | qemu-system-aarch64 -kernel $(KERNEL) -M raspi3 -serial null -serial mon:stdio 53 | 54 | test: $(RUST_DEBUG_LIB) | $(BUILD_DIR) 55 | for test in $(TEST_KERNEL) ; do \ 56 | cp $(RUST_BUILD_DIR)/debug/$$test $(BUILD_DIR)/$$test ; \ 57 | $(OBJCOPY) $(OBJCOPY_PARAMS) $(BUILD_DIR)/$$test $(BUILD_DIR)/$$test.img ; \ 58 | done 59 | 60 | 61 | clean: 62 | $(CARGO) clean 63 | rm -rf $(BUILD_DIR) 64 | -------------------------------------------------------------------------------- /src/arch/aarch64/context/stack.rs: -------------------------------------------------------------------------------- 1 | use alloc::alloc::{GlobalAlloc, Layout}; 2 | use core::ptr::Unique; 3 | use memory::virtual_mem::PhysicalAddr; 4 | //use ALLOCATOR; 5 | use HEAP_ALLOCATOR; 6 | pub struct Stack { 7 | ptr: Unique<[u8; Stack::SIZE]>, 8 | } 9 | 10 | impl Stack { 11 | pub const SIZE: usize = 1 << 20; 12 | pub const ALIGN: usize = 16; 13 | 14 | fn layout() -> Layout { 15 | unsafe { Layout::from_size_align_unchecked(Self::SIZE, Self::ALIGN) } 16 | } 17 | 18 | pub fn new() -> Option { 19 | let raw_ptr = unsafe { 20 | let raw_ptr: *mut u8 = HEAP_ALLOCATOR.alloc(Stack::layout()); 21 | if raw_ptr == (0 as *mut u8) { 22 | return None; 23 | } 24 | raw_ptr.write_bytes(0, Self::SIZE); 25 | raw_ptr 26 | }; 27 | let ptr = Unique::new(raw_ptr as *mut _).expect("non-null"); 28 | Some(Stack { ptr: ptr }) 29 | } 30 | 31 | unsafe fn as_mut_ptr(&self) -> *mut u8 { 32 | self.ptr.as_ptr() as *mut u8 33 | } 34 | 35 | pub fn top(&self) -> PhysicalAddr { 36 | unsafe { self.as_mut_ptr().add(Self::SIZE).into() } 37 | } 38 | 39 | pub fn bottom(&self) -> PhysicalAddr { 40 | unsafe { self.as_mut_ptr().into() } 41 | } 42 | } 43 | 44 | impl Drop for Stack { 45 | fn drop(&mut self) { 46 | unsafe { HEAP_ALLOCATOR.dealloc(self.as_mut_ptr(), Self::layout()) } 47 | } 48 | } 49 | 50 | use core::fmt; 51 | 52 | impl fmt::Debug for Stack { 53 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 54 | f.debug_struct("Stack") 55 | .field("top", &self.top()) 56 | .field("bottom", &self.bottom()) 57 | .field("size", &Self::SIZE) 58 | .finish() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/arch/aarch64/context/process.rs: -------------------------------------------------------------------------------- 1 | use super::stack::Stack; 2 | use alloc::boxed::Box; 3 | use arch::exceptions::TrapFrame; 4 | use core::mem::replace; 5 | 6 | pub type Id = u64; 7 | 8 | pub type EventPollFn = Box bool + Send>; 9 | 10 | pub enum State { 11 | Ready, 12 | Waiting(EventPollFn), 13 | Running, 14 | } 15 | 16 | use core::fmt; 17 | 18 | impl fmt::Debug for State { 19 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 20 | match *self { 21 | State::Ready => write!(f, "State::Ready"), 22 | State::Running => write!(f, "State::Running"), 23 | State::Waiting(_) => write!(f, "State::Waiting"), 24 | } 25 | } 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct Process { 30 | pub trap_frame: Box, 31 | pub stack: Stack, 32 | pub state: State, 33 | } 34 | 35 | impl Process { 36 | pub fn new() -> Option { 37 | let stack = Stack::new(); 38 | match stack { 39 | None => return None, 40 | _ => {} 41 | } 42 | Some(Process { 43 | trap_frame: Box::new(TrapFrame::default()), 44 | stack: stack.unwrap(), 45 | state: State::Ready, 46 | }) 47 | } 48 | 49 | pub fn is_ready(&mut self) -> bool { 50 | true 51 | // let mut mut_state = replace(&mut self.state, State::Ready); 52 | // match &mut mut_state { 53 | // State::Ready => true, 54 | // State::Waiting(ref mut func) => { 55 | // if !func(self) { 56 | // self.state = mut_state; 57 | // false 58 | // } else { 59 | // true 60 | // } 61 | // } 62 | // _ => false, 63 | // } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/io/mini_uart.rs: -------------------------------------------------------------------------------- 1 | use super::gpio; 2 | use super::IO_BASE; 3 | use volatile::*; 4 | 5 | /// Base address for the MU regsiters 6 | const MU_REG_BASE: usize = IO_BASE + 0x215040; 7 | 8 | /// Address of `AUXENB` register 9 | const AUX_ENABLES: *mut Volatile = (IO_BASE + 0x215004) as *mut Volatile; 10 | 11 | #[allow(non_snake_case)] 12 | #[repr(C)] 13 | struct Registers { 14 | IO: Volatile, 15 | IER: Volatile, 16 | IIR: Volatile, 17 | LCR: Volatile, 18 | MCR: Volatile, 19 | LSR: Volatile, 20 | MSR: Volatile, 21 | SCRATCH: Volatile, 22 | CNTL: Volatile, 23 | STAT: Volatile, 24 | BAUD: Volatile, 25 | } 26 | 27 | /// MiniUart strucutre for the RPi3 28 | pub struct MiniUart { 29 | registers: &'static mut Registers, 30 | timeout: Option, 31 | } 32 | 33 | impl MiniUart { 34 | /// Initialize the MiniUart by enabling pin 14 and 15 to alt5 35 | pub fn new() -> MiniUart { 36 | let _gpio14 = gpio::Gpio::new(14).into_alt(gpio::Function::Alt5); 37 | let mut gpio15 = gpio::Gpio::new(15).into_alt(gpio::Function::Alt5); 38 | gpio15.set_gpio(0, (1 << 14) | (1 << 15), 0); 39 | 40 | let registers = unsafe { 41 | (*AUX_ENABLES).update(|v| *v |= 1); 42 | &mut *(MU_REG_BASE as *mut Registers) 43 | }; 44 | 45 | registers.CNTL.write(0); 46 | registers.IER.write(0); 47 | registers.LCR.write(3); 48 | registers.MCR.write(0); 49 | registers.BAUD.write(270); 50 | registers.CNTL.write(3); 51 | 52 | MiniUart { 53 | registers: registers, 54 | timeout: None, 55 | } 56 | } 57 | 58 | /// Writes the byte `byte` to the serial port 59 | pub fn write_byte(&mut self, byte: u8) { 60 | loop { 61 | if self.registers.LSR.read() & 0x20 != 0 { 62 | break; 63 | } 64 | } 65 | self.registers.IO.write(byte as u32); 66 | } 67 | 68 | /// Reads a byte from the serial port and returns it 69 | pub fn read_byte(&mut self) -> u8 { 70 | loop { 71 | if self.registers.LSR.read() & 0x01 != 0 { 72 | break; 73 | } 74 | } 75 | (self.registers.IO.read() & 0xFF) as u8 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/io/console.rs: -------------------------------------------------------------------------------- 1 | use super::mini_uart; 2 | use core::fmt; 3 | use spin::Mutex; 4 | 5 | /// Struct to get a global Console interface 6 | pub struct Console { 7 | mu: Option, 8 | } 9 | 10 | impl Console { 11 | /// Creates a new `Console` 12 | pub fn new() -> Console { 13 | Console { mu: None } 14 | } 15 | 16 | /// Init a newly created console, can only be called once. 17 | pub fn init(&mut self) { 18 | assert_has_not_been_called!("Can only init the Console once"); 19 | self.mu = Some(mini_uart::MiniUart::new()); 20 | } 21 | 22 | /// Writes the byte `byte` to the MiniUart 23 | pub fn write_byte(&mut self, byte: u8) { 24 | match &mut self.mu { 25 | Some(mu) => mu.write_byte(byte), 26 | None => panic!("Console is not initialized"), 27 | } 28 | } 29 | 30 | /// Writes the string `string` to the MiniUart 31 | pub fn write_string(&mut self, string: &str) { 32 | for byte in string.bytes() { 33 | match byte { 34 | // if it is printable 35 | 0x20...0x7e | b'\n' => self.write_byte(byte), 36 | // if not printable 37 | _ => self.write_byte(0xfe), 38 | } 39 | } 40 | } 41 | 42 | /// Reads a byte fron the MiniUart and returns it 43 | pub fn read_byte(&mut self) -> u8 { 44 | match &mut self.mu { 45 | Some(mu) => return mu.read_byte(), 46 | None => panic!("Console is not initialized"), 47 | } 48 | } 49 | } 50 | 51 | impl fmt::Write for Console { 52 | fn write_str(&mut self, s: &str) -> fmt::Result { 53 | self.write_string(s); 54 | Ok(()) 55 | } 56 | } 57 | 58 | /// Global `Console` struct 59 | lazy_static! { 60 | pub static ref CONSOLE: Mutex = Mutex::new(Console::new()); 61 | } 62 | #[macro_export] 63 | macro_rules! kprint { 64 | ($($arg:tt)*) => ($crate::io::console::kprint(format_args!($($arg)*))); 65 | } 66 | 67 | #[macro_export] 68 | macro_rules! kprintln { 69 | () => (kprint!("\n")); 70 | ($fmt:expr) => (kprint!(concat!($fmt, "\n"))); 71 | ($fmt:expr, $($arg:tt)*) => (kprint!(concat!($fmt, "\n"), $($arg)*)); 72 | } 73 | 74 | pub fn kprint(args: fmt::Arguments) { 75 | use core::fmt::Write; 76 | CONSOLE.lock().write_fmt(args).unwrap(); 77 | } 78 | -------------------------------------------------------------------------------- /src/arch/aarch64/exceptions/interrupt.rs: -------------------------------------------------------------------------------- 1 | use io::IO_BASE; 2 | 3 | use volatile::*; 4 | 5 | const INT_BASE: usize = IO_BASE + 0xB000 + 0x200; 6 | 7 | #[derive(Debug, Copy, Clone, PartialEq)] 8 | pub enum Interrupt { 9 | Timer1 = 1, 10 | Timer3 = 3, 11 | Usb = 9, 12 | AuxInt = 29, 13 | I2cSpiSlv = 43, 14 | Pwa0 = 45, 15 | Pwa1 = 46, 16 | Smi = 48, 17 | Gpio0 = 49, 18 | Gpio1 = 50, 19 | Gpio2 = 51, 20 | Gpio3 = 52, 21 | I2c = 53, 22 | Spi = 54, 23 | Pcm = 55, 24 | Uart = 56, 25 | } 26 | 27 | #[repr(C)] 28 | #[allow(non_snake_case)] 29 | struct Registers { 30 | IRQ_BASIC_PENDING: Volatile, 31 | IRQ_PENDING_1: Volatile, 32 | IRQ_PENDING_2: Volatile, 33 | FIQ_CONTROL: Volatile, 34 | ENABLE_IRQ_1: Volatile, 35 | ENABLE_IRQ_2: Volatile, 36 | ENABLE_BASIC_IRQ: Volatile, 37 | DISABLE_IRQ_1: Volatile, 38 | DISABLE_IRQ_2: Volatile, 39 | DISABLE_BASIC_IRQ: Volatile, 40 | } 41 | 42 | pub struct Controller { 43 | registers: &'static mut Registers, 44 | } 45 | 46 | impl Controller { 47 | pub fn new() -> Controller { 48 | Controller { 49 | registers: unsafe { &mut *(INT_BASE as *mut Registers) }, 50 | } 51 | } 52 | 53 | pub fn enable(&mut self, int: Interrupt) { 54 | if (int as u32) < 32 { 55 | self.registers 56 | .ENABLE_IRQ_1 57 | .update(|v| *v ^= 1 << (int as u32)) 58 | } else { 59 | self.registers 60 | .ENABLE_IRQ_2 61 | .update(|v| *v ^= 1 << (int as u32 - 32)) 62 | } 63 | } 64 | 65 | pub fn disable(&mut self, int: Interrupt) { 66 | if (int as u32) < 32 { 67 | self.registers 68 | .DISABLE_IRQ_1 69 | .update(|v| *v ^= 1 << (int as u32)) 70 | } else { 71 | self.registers 72 | .DISABLE_IRQ_2 73 | .update(|v| *v ^= 1 << (int as u32 - 32)) 74 | } 75 | } 76 | 77 | pub fn is_pending(&self, int: Interrupt) -> bool { 78 | if (int as u32) < 32 { 79 | let ret: u32 = self.registers.IRQ_PENDING_1.read() & (1 << (int as u32)); 80 | if ret == 0 { 81 | return true; 82 | } 83 | ret & (ret - 1) == 0 84 | } else { 85 | let ret: u32 = self.registers.IRQ_PENDING_2.read() & (1 << (int as u32 - 32)); 86 | ret & (ret - 1) == 0 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/memory/atags/atag.rs: -------------------------------------------------------------------------------- 1 | use memory::atags::raw; 2 | 3 | pub use memory::atags::raw::{Core, Mem}; 4 | 5 | /// An ATAG 6 | #[derive(Debug, Copy, Clone)] 7 | pub enum Atag { 8 | Core(raw::Core), 9 | Mem(raw::Mem), 10 | Cmd(&'static str), 11 | Unknown(u32), 12 | None, 13 | } 14 | 15 | impl Atag { 16 | /// Returns `Some` if it is a `Core` ATAG, `None` otherwise 17 | pub fn core(&self) -> Option { 18 | match self { 19 | Atag::Core(core) => return Some(*core), 20 | _ => return None, 21 | } 22 | } 23 | 24 | /// Returns `Some` if it is a `Mem` ATAG, `None` otherwise 25 | pub fn mem(&self) -> Option { 26 | match self { 27 | Atag::Mem(mem) => return Some(*mem), 28 | _ => return None, 29 | } 30 | } 31 | 32 | /// Returns `Some` if it is a `Cmd` ATAG, with the command string, `None` otherwise 33 | pub fn cmd(&self) -> Option<&'static str> { 34 | match self { 35 | Atag::Cmd(cmd) => Some(cmd), 36 | _ => return None, 37 | } 38 | } 39 | } 40 | 41 | impl<'a> From<&'a raw::RawAtag> for Atag { 42 | fn from(atag: &raw::RawAtag) -> Atag { 43 | unsafe { 44 | match (atag.tag, &atag.kind) { 45 | (raw::RawAtag::CORE, &raw::Kind { core }) => Atag::from(core), 46 | (raw::RawAtag::MEM, &raw::Kind { mem }) => Atag::from(mem), 47 | (raw::RawAtag::CMDLINE, &raw::Kind { ref cmd }) => Atag::from(cmd), 48 | (raw::RawAtag::NONE, _) => Atag::None, 49 | (id, _) => Atag::Unknown(id), 50 | } 51 | } 52 | } 53 | } 54 | 55 | impl<'a> From for Atag { 56 | fn from(atag: raw::Core) -> Atag { 57 | Atag::Core(atag) 58 | } 59 | } 60 | 61 | impl<'a> From for Atag { 62 | fn from(atag: raw::Mem) -> Atag { 63 | Atag::Mem(atag) 64 | } 65 | } 66 | 67 | impl<'a> From<&'a raw::Cmd> for Atag { 68 | fn from(atag: &'a raw::Cmd) -> Atag { 69 | use core::slice; 70 | use core::str; 71 | let mut i: usize = 0; 72 | loop { 73 | unsafe { 74 | if ((atag as *const raw::Cmd as *const u8).offset(i as isize) as u8) == b'\0' { 75 | break; 76 | } 77 | i += 1; 78 | } 79 | } 80 | 81 | unsafe { 82 | Atag::Cmd(str::from_utf8_unchecked(slice::from_raw_parts( 83 | atag as *const raw::Cmd as *const u8, 84 | i, 85 | ))) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "lazy_static" 3 | version = "1.0.1" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "spin 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "linked_list_allocator" 11 | version = "0.6.3" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | dependencies = [ 14 | "spin 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 15 | ] 16 | 17 | [[package]] 18 | name = "once" 19 | version = "0.3.3" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | 22 | [[package]] 23 | name = "register" 24 | version = "0.1.1" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | dependencies = [ 27 | "tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 28 | ] 29 | 30 | [[package]] 31 | name = "rustberry" 32 | version = "0.1.0" 33 | dependencies = [ 34 | "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 38 | "spin 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 39 | "volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 40 | ] 41 | 42 | [[package]] 43 | name = "spin" 44 | version = "0.4.8" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | 47 | [[package]] 48 | name = "tock-registers" 49 | version = "0.1.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | 52 | [[package]] 53 | name = "volatile" 54 | version = "0.2.4" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | 57 | [metadata] 58 | "checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" 59 | "checksum linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "655d57c71827fe0891ce72231b6aa5e14033dae3f604609e6a6f807267c1678d" 60 | "checksum once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "931fb7a4cf34610cf6cbe58d52a8ca5ef4c726d4e2e178abd0dc13a6551c6d73" 61 | "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479" 62 | "checksum spin 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14db77c5b914df6d6173dda9a3b3f5937bd802934fa5edaf934df06a3491e56f" 63 | "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9" 64 | "checksum volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "54d4343a2df2d65144a874f95950754ee7b7e8594f6027aae8c7d0f4858a3fe8" 65 | -------------------------------------------------------------------------------- /src/arch/aarch64/exceptions/esr.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Copy, Clone)] 2 | pub enum Fault { 3 | AddressSize, 4 | Translation, 5 | AccessFlag, 6 | Permission, 7 | Alignment, 8 | TlbConflict, 9 | Other(u8), 10 | } 11 | 12 | impl From for Fault { 13 | fn from(val: u32) -> Fault { 14 | use self::Fault::*; 15 | match val & 0b111100 { 16 | 0b000000 => AddressSize, 17 | 0b000100 => Translation, 18 | 0b001000 => AccessFlag, 19 | 0b001100 => Permission, 20 | 0b110000 => TlbConflict, 21 | 0b100001 => Alignment, 22 | _ => Other(val as u8), 23 | } 24 | } 25 | } 26 | 27 | #[derive(Debug, PartialEq, Copy, Clone)] 28 | pub enum Syndrome { 29 | Unknown, 30 | WfiWfe, 31 | McrMrc, 32 | McrrMrrc, 33 | LdcStc, 34 | SimdFp, 35 | Vmrs, 36 | Mrrc, 37 | IllegalExecutionState, 38 | Svc(u16), 39 | Hvc(u16), 40 | Smc(u16), 41 | MsrMrsSystem, 42 | InstructionAbort { kind: Fault, level: u8 }, 43 | PCAlignmentFault, 44 | DataAbort { kind: Fault, level: u8 }, 45 | SpAlignmentFault, 46 | TrappedFpu, 47 | SError, 48 | Breakpoint, 49 | Step, 50 | Watchpoint, 51 | Brk(u16), 52 | Other(u32), 53 | } 54 | 55 | impl From for Syndrome { 56 | fn from(esr: u32) -> Syndrome { 57 | use self::Fault; 58 | use self::Syndrome::*; 59 | 60 | match (esr >> 26) as u8 { 61 | 0b000000 => Unknown, 62 | 0b000001 => WfiWfe, 63 | 0b000011 | 0b000101 => McrMrc, 64 | 0b000100 => McrrMrrc, 65 | 0b000110 => LdcStc, 66 | 0b000111 => SimdFp, 67 | 0b001000 => Vmrs, 68 | 0b001100 => Mrrc, 69 | 0b001110 => IllegalExecutionState, 70 | 0b010101 => Svc(esr as u16 & 0xFF), 71 | 0b010110 => Hvc(esr as u16 & 0xFF), 72 | 0b010111 => Smc(esr as u16 & 0xFF), 73 | 0b011000 => MsrMrsSystem, 74 | 0b100000 => InstructionAbort { 75 | kind: Fault::from(esr), 76 | level: esr as u8 & 0b11, 77 | }, 78 | 0b100001 => InstructionAbort { 79 | kind: Fault::from(esr), 80 | level: esr as u8 & 0b11, 81 | }, 82 | 0b100010 => PCAlignmentFault, 83 | 0b100100 => DataAbort { 84 | kind: Fault::from(esr), 85 | level: esr as u8 & 0b11, 86 | }, 87 | 0b100101 => DataAbort { 88 | kind: Fault::from(esr), 89 | level: esr as u8 & 0b11, 90 | }, 91 | 0b100110 => SpAlignmentFault, 92 | 0b101100 => TrappedFpu, 93 | 0b101111 => SError, 94 | 0b110000 | 0b110001 => Breakpoint, 95 | 0b110010 | 0b110011 => Step, 96 | 0b110100 | 0b110101 => Watchpoint, 97 | 0b111100 => Brk(esr as u16 & 0xFF), 98 | _ => Other(esr), 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/io/gpio.rs: -------------------------------------------------------------------------------- 1 | use super::IO_BASE; 2 | use core::marker::PhantomData; 3 | use volatile::*; 4 | 5 | /// Base address of `GPIO` registers 6 | const GPIO_BASE: usize = IO_BASE + 0x200000; 7 | 8 | /// Alternative GPIO functions 9 | #[repr(u8)] 10 | pub enum Function { 11 | Input = 0b000, 12 | Output = 0b001, 13 | Alt0 = 0b100, 14 | Alt1 = 0b101, 15 | Alt2 = 0b110, 16 | Alt3 = 0b111, 17 | Alt4 = 0b011, 18 | Alt5 = 0b010, 19 | } 20 | 21 | #[repr(C)] 22 | #[allow(non_snake_case)] 23 | struct Registers { 24 | FSEL: [Volatile; 6], 25 | __reserved0: u32, 26 | SET: [WriteOnly; 2], 27 | __reserved1: u32, 28 | CLR: [WriteOnly; 2], 29 | __reserved2: u32, 30 | LEV: [ReadOnly; 2], 31 | __reserved3: u32, 32 | EDS: [Volatile; 2], 33 | __reserved4: u32, 34 | REN: [Volatile; 2], 35 | __reserved5: u32, 36 | FEN: [Volatile; 2], 37 | __reserved6: u32, 38 | HEN: [Volatile; 2], 39 | __reserved7: u32, 40 | LEN: [Volatile; 2], 41 | __reserved8: u32, 42 | AREN: [Volatile; 2], 43 | __reserved9: u32, 44 | AFEN: [Volatile; 2], 45 | __reserved10: u32, 46 | PUD: Volatile, 47 | PUDCLK: [Volatile; 2], 48 | } 49 | 50 | /// States of a pin 51 | pub enum Uninitialized {} 52 | pub enum Input {} 53 | pub enum Output {} 54 | pub enum Alt {} 55 | 56 | /// A Gpio pin in the state `State` 57 | pub struct Gpio { 58 | pin: u8, 59 | registers: &'static mut Registers, 60 | _state: PhantomData, 61 | } 62 | 63 | impl Gpio { 64 | /// Transition `self` to a new state `S` 65 | #[inline(always)] 66 | fn transition(self) -> Gpio { 67 | Gpio { 68 | pin: self.pin, 69 | registers: self.registers, 70 | _state: PhantomData, 71 | } 72 | } 73 | 74 | /// Set the Gpio pull-up/pull-down state for values in `pin_value` 75 | pub fn set_gpio(&mut self, gpio_value: u8, pin_value: u32, pudclk_index: u8) { 76 | unsafe { 77 | self.registers.PUD.write(gpio_value as u32); 78 | for _ in 0..150 { 79 | asm!("nop" :::: "volatile"); 80 | } 81 | 82 | self.registers.PUDCLK[pudclk_index as usize].write(pin_value as u32); 83 | for _ in 0..150 { 84 | asm!("nop" :::: "volatile"); 85 | } 86 | self.registers.PUDCLK[pudclk_index as usize].write(0); 87 | } 88 | } 89 | } 90 | 91 | impl Gpio { 92 | /// Returns a new `Uninitialized` `GPIO` struct for pin `pin` 93 | pub fn new(pin: u8) -> Gpio { 94 | if pin > 53 { 95 | panic!("Gpio::new(): pin {} exceeds maximum of 53", pin); 96 | } 97 | 98 | Gpio { 99 | registers: unsafe { &mut *(GPIO_BASE as *mut Registers) }, 100 | pin: pin, 101 | _state: PhantomData, 102 | } 103 | } 104 | 105 | /// Enables the alt function `function` 106 | pub fn into_alt(self, function: Function) -> Gpio { 107 | let select = self.pin / 10; 108 | let offset = self.pin % 10; 109 | 110 | self.registers.FSEL[select as usize].write((function as u32) << (3 * offset)); 111 | Gpio { 112 | pin: self.pin, 113 | registers: self.registers, 114 | _state: PhantomData, 115 | } 116 | } 117 | 118 | /// Sets the pin to an `Output` pin 119 | pub fn into_output(self) -> Gpio { 120 | self.into_alt(Function::Output).transition() 121 | } 122 | 123 | /// Sets the pin to an `Input` pin 124 | pub fn into_input(self) -> Gpio { 125 | self.into_alt(Function::Input).transition() 126 | } 127 | } 128 | 129 | impl Gpio { 130 | /// Sets the pin 131 | pub fn set(&mut self) { 132 | let index = if self.pin >= 32 { 1 } else { 0 }; 133 | self.registers.SET[index as usize].write(1 << (self.pin - index * 32)); 134 | } 135 | 136 | /// Clear the pin 137 | pub fn clear(&mut self) { 138 | let index = if self.pin >= 32 { 1 } else { 0 }; 139 | self.registers.CLR[index as usize].write(1 << (self.pin - index * 32)); 140 | } 141 | } 142 | 143 | impl Gpio { 144 | /// Gets the pin value, returns `true` if the level is high, `false` if it is low 145 | pub fn level(&mut self) -> bool { 146 | let index = if self.pin >= 32 { 1 } else { 0 }; 147 | let high = 1 << (self.pin - index * 32); 148 | (self.registers.LEV[index as usize].read() & high) == high 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/arch/aarch64/context/scheduler.rs: -------------------------------------------------------------------------------- 1 | use super::process::{Id, Process, State}; 2 | use alloc::collections::vec_deque::VecDeque; 3 | use arch::exceptions::TrapFrame; 4 | use spin::Mutex; 5 | 6 | pub const TICK: u32 = 2 * 1000 * 1000; 7 | 8 | #[derive(Debug)] 9 | pub struct GlobalScheduler(Mutex>); 10 | 11 | impl GlobalScheduler { 12 | pub const fn uninitialized() -> GlobalScheduler { 13 | GlobalScheduler(Mutex::new(None)) 14 | } 15 | 16 | pub fn add(&self, process: Process) -> Option { 17 | self.0 18 | .lock() 19 | .as_mut() 20 | .expect("scheduler uninitialized") 21 | .add(process) 22 | } 23 | 24 | #[must_use] 25 | pub fn switch(&self, new_state: State, tf: &mut TrapFrame) -> Option { 26 | self.0 27 | .lock() 28 | .as_mut() 29 | .expect("scheduler uninitialized") 30 | .switch(new_state, tf) 31 | } 32 | 33 | pub fn start(&self) { 34 | *self.0.lock() = Some(Scheduler::new()); 35 | let mut p1 = Process::new().expect("failed to create process p1"); 36 | p1.trap_frame.elr = proc1 as *mut u64 as u64; 37 | p1.trap_frame.sp = p1.stack.top().as_u64(); 38 | p1.trap_frame.spsr = 1; 39 | let tr = &*p1.trap_frame as *const _; 40 | self.add(p1); 41 | let mut p2 = Process::new().expect("failed to create process p2"); 42 | p2.trap_frame.elr = proc2 as *mut u64 as u64; 43 | p2.trap_frame.sp = p2.stack.top().as_u64(); 44 | self.add(p2); 45 | let mut p3 = Process::new().expect("failed to create process p3"); 46 | p3.trap_frame.elr = proc3 as *mut u64 as u64; 47 | p3.trap_frame.sp = p3.stack.top().as_u64(); 48 | self.add(p3); 49 | 50 | use io; 51 | io::generic_timer::GenericTimer::new().init(); 52 | unsafe { 53 | asm!(" mov x0, $0 54 | mov sp, x0 55 | bl context_restore 56 | adr x0, _start 57 | mov sp, x0 58 | eret" 59 | :: "r"(tr) 60 | :: "volatile"); 61 | } 62 | } 63 | } 64 | extern "C" fn proc1() { 65 | kprintln!("Hey proc1"); 66 | unsafe { 67 | asm!("brk 1" :::: "volatile"); 68 | } 69 | loop { 70 | unsafe { 71 | kprintln!("Hey proc1"); 72 | asm!("nop" :::: "volatile"); 73 | for _ in 1..99999 { 74 | asm!("nop" :::: "volatile"); 75 | } 76 | } 77 | } 78 | } 79 | 80 | extern "C" fn proc3() { 81 | kprintln!("Hey proc3"); 82 | unsafe { 83 | asm!("brk 3" :::: "volatile"); 84 | } 85 | loop { 86 | unsafe { 87 | kprintln!("Hey proc3"); 88 | asm!("nop" :::: "volatile"); 89 | for _ in 1..99999 { 90 | asm!("nop" :::: "volatile"); 91 | } 92 | } 93 | } 94 | } 95 | extern "C" fn proc2() { 96 | kprintln!("Hey proc2"); 97 | unsafe { 98 | asm!("brk 2" :::: "volatile"); 99 | } 100 | loop { 101 | unsafe { 102 | kprintln!("Hey proc2"); 103 | asm!("nop" :::: "volatile"); 104 | for _ in 1..99999 { 105 | asm!("nop" :::: "volatile"); 106 | } 107 | } 108 | } 109 | } 110 | 111 | #[derive(Debug)] 112 | struct Scheduler { 113 | processes: VecDeque, 114 | current: Option, 115 | last_id: Option, 116 | } 117 | 118 | impl Scheduler { 119 | fn new() -> Scheduler { 120 | Scheduler { 121 | processes: VecDeque::new(), 122 | current: None, 123 | last_id: None, 124 | } 125 | } 126 | 127 | fn add(&mut self, mut process: Process) -> Option { 128 | if self.last_id.is_none() { 129 | self.last_id = Some(0); 130 | self.current = Some(0); 131 | } else { 132 | self.last_id = Some(self.last_id.unwrap() + 1); 133 | } 134 | process.trap_frame.tpidr = self.last_id.unwrap(); 135 | self.processes.push_back(process); 136 | self.last_id 137 | } 138 | 139 | fn switch(&mut self, new_state: State, tf: &mut TrapFrame) -> Option { 140 | if self.processes.is_empty() { 141 | unsafe { 142 | asm!("wfi" :::: "volatile"); 143 | } 144 | } 145 | let mut proc = self.processes.pop_front().expect("1"); 146 | proc.state = new_state; 147 | *proc.trap_frame = *tf; 148 | 149 | self.processes.push_back(proc); 150 | loop { 151 | let mut new_proc = self.processes.pop_front().expect("2"); 152 | if new_proc.is_ready() { 153 | new_proc.state = State::Running; 154 | self.current = Some(new_proc.trap_frame.tpidr); 155 | *tf = *new_proc.trap_frame; 156 | self.processes.push_front(new_proc); 157 | break; 158 | } else { 159 | self.processes.push_back(new_proc); 160 | } 161 | } 162 | Some(self.current.expect("3")) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/asm/init.S: -------------------------------------------------------------------------------- 1 | #define EL0 0b00 2 | #define EL1 0b01 3 | #define EL2 0b10 4 | #define EL3 0b11 5 | 6 | .section .text.init 7 | 8 | .global _start 9 | _start: 10 | // read cpu affinity, start core 0, halt rest 11 | mrs x1, MPIDR_EL1 12 | and x1, x1, #3 13 | cbz x1, setup 14 | 15 | halt: 16 | // core affinity != 0, halt it 17 | wfe 18 | b halt 19 | 20 | setup: 21 | // store the desired EL1 stack pointer in x1 22 | adr x1, _start 23 | 24 | // read the current exception level into x0 (ref: C5.2.1) 25 | mrs x0, CurrentEL 26 | and x0, x0, #0b1100 27 | lsr x0, x0, #2 28 | 29 | switch_to_el2: 30 | // switch to EL2 if we're in EL3. otherwise switch to EL1 31 | cmp x0, 0b11 32 | bne switch_to_el1 33 | 34 | // set-up SCR_EL3 (bits 0, 4, 5, 7, 8, 10) (A53: 4.3.42) 35 | mov x2, #0x5b1 36 | msr SCR_EL3, x2 37 | 38 | // set-up SPSR and PL switch! (bits 0, 3, 6, 7, 8, 9) (ref: C5.2.20) 39 | mov x2, #0x3c9 40 | msr SPSR_EL3, x2 41 | adr x2, switch_to_el1 42 | msr ELR_EL3, x2 43 | eret 44 | 45 | switch_to_el1: 46 | // switch to EL1 if we're not already in EL1. otherwise continue with start 47 | cmp x0, 0b01 48 | beq set_stack 49 | 50 | // set the stack-pointer for EL1 51 | msr SP_EL1, x1 52 | 53 | // enable CNTP for EL1/EL0 (ref: D7.5.2, D7.5.13) 54 | // NOTE: This doesn't actually enable the counter stream. 55 | mrs x0, CNTHCTL_EL2 56 | orr x0, x0, #0b11 57 | msr CNTHCTL_EL2, x0 58 | msr CNTVOFF_EL2, xzr 59 | 60 | // enable AArch64 in EL1 (A53: 4.3.36) 61 | mov x0, #(1 << 31) // Enable AArch64 for EL1 62 | orr x0, x0, #(1 << 1) // RES1 on A-53 63 | msr HCR_EL2, x0 64 | mrs x0, HCR_EL2 65 | 66 | // enable floating point and SVE (SIMD) (A53: 4.3.38, 4.3.34) 67 | msr CPTR_EL2, xzr // don't trap accessing SVE registers 68 | mrs x0, CPACR_EL1 69 | orr x0, x0, #(0b11 << 20) 70 | msr CPACR_EL1, x0 71 | 72 | // Set SCTLR to known state (RES1: 11, 20, 22, 23, 28, 29) (A53: 4.3.30) 73 | mov x2, #0x0800 74 | movk x2, #0x30d0, lsl #16 75 | msr SCTLR_EL1, x2 76 | 77 | // set up exception handlers 78 | // FIXME: load `_vectors` addr into appropriate register (guide: 10.4) 79 | adr x0, _vectors 80 | msr vbar_el1, x0 81 | 82 | 83 | // change execution level to EL1 (ref: C5.2.19) 84 | mov x2, #0x3c5 85 | msr SPSR_EL2, x2 86 | 87 | adr x0, set_stack 88 | msr elr_el2, x0 89 | 90 | eret 91 | 92 | set_stack: 93 | // set the current stack pointer 94 | mov sp, x1 95 | 96 | zero_bss: 97 | // load the start address and number of bytes in BSS section 98 | ldr x1, =__bss_start 99 | ldr x2, =__bss_length 100 | 101 | zero_bss_loop: 102 | // zero out the BSS section, 64-bits at a time 103 | cbz x2, go_kmain 104 | str xzr, [x1], #8 105 | sub x2, x2, #8 106 | cbnz x2, zero_bss_loop 107 | 108 | go_kmain: 109 | // jump to kmain, which shouldn't return. halt if it does 110 | bl main 111 | b halt 112 | 113 | context_save: 114 | sub sp, sp, #16*33 115 | stp q0, q1, [sp, #16*2] 116 | stp q2, q3, [sp, #16*3] 117 | stp q4, q5, [sp, #16*4] 118 | stp q6, q7, [sp, #16*5] 119 | stp q8, q9, [sp, #16*6] 120 | stp q10, q11, [sp, #16*7] 121 | stp q12, q13, [sp, #16*8] 122 | stp q14, q15, [sp, #16*9] 123 | stp q16, q17, [sp, #16*10] 124 | stp q18, q19, [sp, #16*11] 125 | stp q20, q21, [sp, #16*12] 126 | stp q22, q23, [sp, #16*13] 127 | stp q24, q25, [sp, #16*14] 128 | stp q26, q27, [sp, #16*15] 129 | stp q28, q29, [sp, #16*16] 130 | stp q30, q31, [sp, #16*17] 131 | stp x1, x2, [sp, #16*18] 132 | stp x3, x4, [sp, #16*19] 133 | stp x5, x6, [sp, #16*20] 134 | stp x7, x8, [sp, #16*21] 135 | stp x9, x10, [sp, #16*22] 136 | stp x11, x12, [sp, #16*23] 137 | stp x13, x14, [sp, #16*24] 138 | stp x15, x16, [sp, #16*25] 139 | stp x17, x18, [sp, #16*26] 140 | stp x19, x20, [sp, #16*27] 141 | stp x21, x22, [sp, #16*28] 142 | stp x23, x24, [sp, #16*29] 143 | stp x25, x26, [sp, #16*30] 144 | stp x27, x28, [sp, #16*31] 145 | str x29, [sp, #16*32] 146 | 147 | mrs x1, elr_el1 148 | mrs x2, spsr_el1 149 | stp x1, x2, [sp, #16*0] 150 | mrs x1, sp_el0 151 | mrs x2, tpidr_el0 152 | stp x1, x2, [sp, #16*1] 153 | 154 | mrs x1, esr_el1 155 | mov x2, sp 156 | sub sp, sp, #16*1 157 | str lr, [sp, #16*0] 158 | bl handle_exception 159 | ldr lr, [sp, #16*0] 160 | add sp, sp, #16*1 161 | 162 | .global context_restore 163 | context_restore: 164 | ldp x1, x2, [sp, #16*1] 165 | msr tpidr_el0, x2 166 | msr sp_el0, x1 167 | ldp x1, x2, [sp, #16*0] 168 | msr spsr_el1, x2 169 | msr elr_el1, x1 170 | 171 | ldr x29, [sp, #16*32] 172 | ldp x27, x28, [sp, #16*31] 173 | ldp x25, x26, [sp, #16*30] 174 | ldp x23, x24, [sp, #16*29] 175 | ldp x21, x22, [sp, #16*28] 176 | ldp x19, x20, [sp, #16*27] 177 | ldp x17, x18, [sp, #16*26] 178 | ldp x15, x16, [sp, #16*25] 179 | ldp x13, x14, [sp, #16*24] 180 | ldp x11, x12, [sp, #16*23] 181 | ldp x9, x10, [sp, #16*22] 182 | ldp x7, x8, [sp, #16*21] 183 | ldp x5, x6, [sp, #16*20] 184 | ldp x3, x4, [sp, #16*19] 185 | ldp x1, x2, [sp, #16*18] 186 | ldp q30, q31, [sp, #16*17] 187 | ldp q28, q29, [sp, #16*16] 188 | ldp q26, q27, [sp, #16*15] 189 | ldp q24, q25, [sp, #16*14] 190 | ldp q22, q23, [sp, #16*13] 191 | ldp q20, q21, [sp, #16*12] 192 | ldp q18, q19, [sp, #16*11] 193 | ldp q16, q17, [sp, #16*10] 194 | ldp q14, q15, [sp, #16*9] 195 | ldp q12, q13, [sp, #16*8] 196 | ldp q10, q11, [sp, #16*7] 197 | ldp q8, q9, [sp, #16*6] 198 | ldp q6, q7, [sp, #16*5] 199 | ldp q4, q5, [sp, #16*4] 200 | ldp q2, q3, [sp, #16*3] 201 | ldp q0, q1, [sp, #16*2] 202 | add sp, sp, #16*33 203 | 204 | ret 205 | 206 | 207 | 208 | .macro HANDLER source kind 209 | .align 7 210 | stp lr, x0, [SP, #-16]! 211 | mov x0, #\source 212 | movk x0, #\kind, LSL #16 213 | bl context_save 214 | ldp lr, x0, [SP], #16 215 | eret 216 | .endm 217 | 218 | 219 | .align 11 220 | _vectors: 221 | HANDLER 0 0 222 | HANDLER 0 1 223 | HANDLER 0 2 224 | HANDLER 0 3 225 | HANDLER 1 0 226 | HANDLER 1 1 227 | HANDLER 1 2 228 | HANDLER 1 3 229 | HANDLER 2 0 230 | HANDLER 2 1 231 | HANDLER 2 2 232 | HANDLER 2 3 233 | HANDLER 3 0 234 | HANDLER 3 1 235 | HANDLER 3 2 236 | HANDLER 3 3 237 | --------------------------------------------------------------------------------