| @@ -11,3 +11,4 @@ panic = "abort" | |||||
| panic = "abort" | panic = "abort" | ||||
| [dependencies] | [dependencies] | ||||
| @@ -1,21 +0,0 @@ | |||||
| // AArch32 mode | |||||
| .section ".text.boot" | |||||
| .globl _start | |||||
| .org 0x8000 | |||||
| _start: | |||||
| mov sp, #0x8000 | |||||
| ldr r4, =__bss_start | |||||
| ldr r9, =__bss_end | |||||
| mov r5, #0 | |||||
| mov r6, #0 | |||||
| mov r7, #0 | |||||
| mov r8, #0 | |||||
| b 2f | |||||
| 1: stmia r4!, {r4-r8} | |||||
| 2: cmp r4, r9 | |||||
| blo 1b | |||||
| ldr r4, =kernel_main | |||||
| blx r3 | |||||
| halt: wfe | |||||
| b halt | |||||
| @@ -1,7 +1,14 @@ | |||||
| # build the OS | |||||
| build-os: | build-os: | ||||
| arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -c asm/boot-2.s -o build/boot.o | |||||
| cargo xbuild --release --target armv7r-none-eabihf | |||||
| arm-none-eabi-gcc -T linker.ld -o build/grove.img -ffreestanding -O2 -nostdlib build/boot.o target/target/release/libgrove.rlib | |||||
| rm build/* | |||||
| arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -c asm/boot.s -o build/boot.o | |||||
| cargo xbuild --target armv7a-none-eabi | |||||
| arm-none-eabi-gcc -T linker.ld -o build/grove.img -ffreestanding -O2 -nostdlib build/boot.o target/armv7a-none-eabi/debug/libgrove.rlib | |||||
| run-os: build-os | |||||
| qemu-system-arm -M raspi2 -kernel build/grove.img -serial stdio | |||||
| # build the OS and run it on QEMU | |||||
| run: build-os | |||||
| qemu-system-arm -M raspi2 -kernel build/grove.img -serial stdio | |||||
| # build just the rust binary | |||||
| build: | |||||
| cargo xbuild --target armv7a-none-eabi | |||||
| @@ -1,51 +1,59 @@ | |||||
| #![no_std] | #![no_std] | ||||
| #![feature(asm)] | |||||
| use core::panic::PanicInfo; | |||||
| use core::ptr::{read_volatile, write_volatile}; | |||||
| const UART_DR: u32 = 0x3f20_1000; | |||||
| const UART_FR: u32 = 0x3f20_1018; | |||||
| fn mmio_write(reg: u32, val: u32) { | |||||
| unsafe { write_volatile(reg as *mut u32, val) } | |||||
| } | |||||
| mod mem; | |||||
| fn mmio_read(reg: u32) -> u32 { | |||||
| unsafe { read_volatile(reg as *const u32) } | |||||
| } | |||||
| use core::panic::PanicInfo; | |||||
| fn transmit_fifo_full() -> bool { | |||||
| mmio_read(UART_FR) & (1 << 5) > 0 | |||||
| /// represents the board type | |||||
| #[derive(PartialEq, Eq)] | |||||
| pub enum BoardType { | |||||
| PiZeroOne, | |||||
| PiTwo, | |||||
| PiThree, | |||||
| PiFour, | |||||
| Unknown, | |||||
| } | } | ||||
| fn receive_fifo_empty() -> bool { | |||||
| mmio_read(UART_FR) & (1 << 4) > 0 | |||||
| } | |||||
| impl BoardType { | |||||
| /// detect the type of Raspberry Pi board | |||||
| pub fn detect() -> Self { | |||||
| let mut reg_data: u32; | |||||
| fn writec(c: u8) { | |||||
| while transmit_fifo_full() {} | |||||
| mmio_write(UART_DR, c as u32); | |||||
| } | |||||
| unsafe { | |||||
| asm!("mrc p15, 0, {}, c0, c0, 0", out(reg) reg_data); | |||||
| } | |||||
| fn getc() -> u8 { | |||||
| while receive_fifo_empty() {} | |||||
| mmio_read(UART_DR) as u8 | |||||
| } | |||||
| match reg_data >> 4 & 0xFFF { | |||||
| 0xB76 => BoardType::PiZeroOne, | |||||
| 0xC07 => BoardType::PiTwo, | |||||
| 0xD03 => BoardType::PiThree, | |||||
| 0xD08 => BoardType::PiFour, | |||||
| _ => BoardType::Unknown, | |||||
| } | |||||
| } | |||||
| fn write(msg: &str) { | |||||
| for c in msg.bytes() { | |||||
| writec(c); | |||||
| /// return the MMIO base address | |||||
| pub fn mmio_base_addr(self) -> *const u32 { | |||||
| match self { | |||||
| BoardType::PiZeroOne | BoardType::Unknown => 0x2000_0000 as *const u32, | |||||
| BoardType::PiTwo | BoardType::PiThree => 0x3F00_0000 as *const u32, | |||||
| BoardType::PiFour => 0xFE00_0000 as *const u32, | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| #[no_mangle] | #[no_mangle] | ||||
| pub extern "C" fn kernel_main() { | pub extern "C" fn kernel_main() { | ||||
| write("hello rust raspberry pi kernel"); | |||||
| loop { | |||||
| writec(getc()) | |||||
| } | |||||
| let board_type = BoardType::detect(); | |||||
| let mem = mem::MemoryMappedIo::init(board_type); | |||||
| mem.write_str("hello grove kernel <3"); | |||||
| loop {} | |||||
| } | } | ||||
| /// these functions are here to make the compiler/linker happy :) | |||||
| #[no_mangle] | #[no_mangle] | ||||
| pub extern "C" fn __aeabi_unwind_cpp_pr0() {} | pub extern "C" fn __aeabi_unwind_cpp_pr0() {} | ||||
| @@ -0,0 +1,80 @@ | |||||
| pub struct MemoryMappedIo { | |||||
| base_addr: *const u32, | |||||
| } | |||||
| impl MemoryMappedIo { | |||||
| pub fn init(board_type: crate::BoardType) -> Self { | |||||
| let mem = MemoryMappedIo { base_addr: board_type.mmio_base_addr() }; | |||||
| mem.uart_init(); | |||||
| mem | |||||
| } | |||||
| fn uart_init(&self) {} | |||||
| // receive fifo empty | |||||
| fn recv_fifo_empty(&self) -> bool { | |||||
| self.read_word(PeripheralAddress::UartFr) & (1 << 5) > 0 | |||||
| } | |||||
| // transmit fifo full | |||||
| fn tran_fifo_full(&self) -> bool { | |||||
| self.read_word(PeripheralAddress::UartFr) & (1 << 4) > 0 | |||||
| } | |||||
| pub fn write_str(&self, s: &str) { | |||||
| s.bytes().for_each(|byte| self.write_byte(byte)) | |||||
| } | |||||
| pub fn write_byte(&self, byte: u8) { | |||||
| while self.tran_fifo_full() {} | |||||
| self.write_word(PeripheralAddress::UartDr, byte as u32); | |||||
| } | |||||
| pub fn get_byte(&self) -> u8 { | |||||
| while self.recv_fifo_empty() {} | |||||
| self.read_word(PeripheralAddress::UartDr) as u8 | |||||
| } | |||||
| // write a word to the memory mapped IO | |||||
| pub fn write_word(&self, peripheral_addr: PeripheralAddress, data: u32) { | |||||
| unsafe { | |||||
| let addr = self.base_addr.offset(peripheral_addr as isize) as *mut u32; | |||||
| addr.write_volatile(data); | |||||
| } | |||||
| } | |||||
| // read a word from memory mapped IO | |||||
| pub fn read_word(&self, peripheral_addr: PeripheralAddress) -> u32 { | |||||
| unsafe { | |||||
| let addr = self.base_addr.offset(peripheral_addr as isize) as *mut u32; | |||||
| addr.read_volatile() | |||||
| } | |||||
| } | |||||
| } | |||||
| /// represents the offset from the base address | |||||
| #[allow(dead_code)] | |||||
| #[repr(isize)] | |||||
| pub enum PeripheralAddress { | |||||
| Gpiobase = 0x0020_0000, | |||||
| Gppud = 0x0020_0094, | |||||
| Gppudclk0 = 0x0020_0098, | |||||
| UartDr = 0x0020_1000, | |||||
| UartRsrecr = 0x0020_1004, | |||||
| UartFr = 0x0020_1018, | |||||
| UartIlpr = 0x0020_1020, | |||||
| UartIbrd = 0x0020_1024, | |||||
| UartFbrd = 0x0020_1028, | |||||
| UartLcrh = 0x0020_102C, | |||||
| UartCr = 0x0020_1030, | |||||
| UartIfls = 0x0020_1034, | |||||
| UartImsc = 0x0020_1038, | |||||
| Uartris = 0x0020_103C, | |||||
| UartMis = 0x0020_1040, | |||||
| UartIcr = 0x0020_1044, | |||||
| UartDmacr = 0x0020_1048, | |||||
| UartItcr = 0x0020_1080, | |||||
| UartItip = 0x0020_1084, | |||||
| UartItop = 0x0020_1088, | |||||
| UartTdr = 0x0020_108C, | |||||
| } | |||||