| @@ -11,3 +11,4 @@ panic = "abort" | |||
| panic = "abort" | |||
| [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: | |||
| 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] | |||
| #![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] | |||
| 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] | |||
| 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, | |||
| } | |||