diff --git a/.gitignore b/.gitignore index b2a1d4c41..427586be8 100644 --- a/.gitignore +++ b/.gitignore @@ -90,4 +90,6 @@ hw0/ *.elf *.img target/ -kernel*.img \ No newline at end of file +kernel*.img + +initramfs.cpio \ No newline at end of file diff --git a/bootloader/Cargo.lock b/bootloader/Cargo.lock new file mode 100644 index 000000000..554f86426 --- /dev/null +++ b/bootloader/Cargo.lock @@ -0,0 +1,26 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aarch64-cpu" +version = "9.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "bootloader" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", + "tock-registers", +] + +[[package]] +name = "tock-registers" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml new file mode 100644 index 000000000..73490eb45 --- /dev/null +++ b/bootloader/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "bootloader" +version = "0.1.0" +authors = ["HelloHomework"] +edition = "2021" + +[[bin]] +name = "bootloader" +path = "src/main.rs" +test = false +bench = false + +[lib] +test = false + +#[toolchain] +#channel = "nightly" + +[features] +default = ["tock-registers"] + +[workspace] +resolver = "2" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aarch64-cpu = "9.4.0" +tock-registers = { version = "0.8.x", default-features = false, features = ["register_types"], optional = true } diff --git a/bootloader/Makefile b/bootloader/Makefile new file mode 100644 index 000000000..55c802c99 --- /dev/null +++ b/bootloader/Makefile @@ -0,0 +1,127 @@ +## SPDX-License-Identifier: MIT OR Apache-2.0 +## +## Copyright (c) 2018-2023 Andre Richter +## +## This file is part of the `rust-raspberrypi-OS-tutorials` project. +## https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials/ +## +##-------------------------------------------------------------------------------------------------- +## Optional, user-provided configuration values +##-------------------------------------------------------------------------------------------------- + +# BSP ?= rpi3 +TARGET = aarch64-unknown-none-softfloat +KERNEL_BIN = bootloader.img +QEMU_BINARY = qemu-system-aarch64 +QEMU_MACHINE_TYPE = raspi3 +QEMU_RELEASE_ARGS = -display none -d in_asm + +# LD_SCRIPT_PATH = src/ +# LD_SCRIPT_NAME = linker.ld +LD_SCRIPT_NAME = kernel.ld + +# LD_SCRIPT_PATH = $(shell pwd)/src/bsp/raspberrypi +LD_SCRIPT_PATH = src/ +KERNEL_LINKER_SCRIPT = $(LD_SCRIPT_PATH)bsp/rpi3/$(LD_SCRIPT_NAME) + + +RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 + +# Export for build.rs. +export LD_SCRIPT_PATH + + + +##-------------------------------------------------------------------------------------------------- +## Targets and Prerequisites +##-------------------------------------------------------------------------------------------------- +KERNEL_MANIFEST = Cargo.toml +# KERNEL_LINKER_SCRIPT = kernel.ld +LAST_BUILD_CONFIG = target/$(BSP).build_config + +KERNEL_ELF = target/$(TARGET)/debug/bootloader +# KERNEL_ELF = target/$(TARGET)/release/kernel +# This parses cargo's dep-info file. +# https://doc.rust-lang.org/cargo/guide/build-cache.html#dep-info-files +KERNEL_ELF_DEPS = $(filter-out %: ,$(file < $(KERNEL_ELF).d)) $(KERNEL_MANIFEST) $(LAST_BUILD_CONFIG) + + + +##-------------------------------------------------------------------------------------------------- +## Command building blocks +##-------------------------------------------------------------------------------------------------- +RUSTFLAGS = $(RUSTC_MISC_ARGS) \ + -C link-arg=--library-path=$(LD_SCRIPT_PATH) \ + -C link-arg=--script=$(KERNEL_LINKER_SCRIPT) + +RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) \ + -D warnings \ + -D missing_docs + +# FEATURES = --features bsp_$(BSP) +COMPILER_ARGS = --target=$(TARGET) \ +# --release + # $(FEATURES)\ + + +RUSTC_CMD = cargo rustc $(COMPILER_ARGS) +# RUSTC_CMD = cargo miri $(COMPILER_ARGS) +OBJCOPY_CMD = rust-objcopy \ + --strip-all \ + -O binary + +##-------------------------------------------------------------------------------------------------- +## Targets +##-------------------------------------------------------------------------------------------------- + +all: $(KERNEL_BIN) + +##------------------------------------------------------------------------------ +## Save the configuration as a file, so make understands if it changed. +##------------------------------------------------------------------------------ +$(LAST_BUILD_CONFIG): + @rm -f target/*.build_config + @mkdir -p target + @touch $(LAST_BUILD_CONFIG) + +##------------------------------------------------------------------------------ +## Compile the kernel ELF +##------------------------------------------------------------------------------ +$(KERNEL_ELF): $(KERNEL_ELF_DEPS) +# $(call color_header, "Compiling kernel ELF - $(BSP)") + @RUSTFLAGS="$(RUSTFLAGS)" $(RUSTC_CMD) + +##------------------------------------------------------------------------------ +## Generate the stripped kernel binary +##------------------------------------------------------------------------------ +$(KERNEL_BIN): $(KERNEL_ELF) + $(call color_header, "Generating stripped binary") + @$(OBJCOPY_CMD) $(KERNEL_ELF) $(KERNEL_BIN) + $(call color_progress_prefix, "Name") + @echo $(KERNEL_BIN) + $(call color_progress_prefix, "Size") + $(call disk_usage_KiB, $(KERNEL_BIN)) + + +##------------------------------------------------------------------------------ +## Debug +##------------------------------------------------------------------------------ +debug: +# qemu-system-aarch64 -M raspi3b -kernel $(KERNEL_BIN) -display none -serial null -serial stdio +# qemu-system-aarch64 -M raspi3b -kernel $(KERNEL_BIN) -display none -serial stdio + qemu-system-aarch64 -M raspi3b -kernel $(KERNEL_BIN) \ + -display none -serial null -serial pty \ + -dtb ../resources/bcm2710-rpi-3-b-plus.dtb \ + -initrd ../resources/initramfs.cpio + +# debug: +# qemu-system-aarch64 -M raspi3b -kernel $(KERNEL_BIN) \ +# -display none -serial null -serial pty \ +# -dtb ../resources/bcm2710-rpi-3-b-plus.dtb \ +# -initrd ../resources/initramfs.cpio + +##------------------------------------------------------------------------------ +## Clean +##------------------------------------------------------------------------------ +clean: + rm -rf target $(KERNEL_BIN) diff --git a/kernel/kernel.ld b/bootloader/kernel.ld similarity index 100% rename from kernel/kernel.ld rename to bootloader/kernel.ld diff --git a/bootloader/src/arrsting.rs b/bootloader/src/arrsting.rs new file mode 100644 index 000000000..93d123059 --- /dev/null +++ b/bootloader/src/arrsting.rs @@ -0,0 +1,51 @@ +// #[derive(PartialEq)] +const MAXIUM_LEN: usize = 256; +pub struct ArrString { + data: [u8; MAXIUM_LEN], // maxium size MAXIUM_LEN + len: usize, +} + +#[allow(dead_code)] +impl ArrString { + pub fn new(s: &str) -> Self { + let mut data: [u8; MAXIUM_LEN] = [0; MAXIUM_LEN]; + for (index, c) in s.chars().enumerate() { + data[index] = c as u8; + } + ArrString { + data: data, + len: s.len(), + } + } + + pub fn get_len(&self) -> usize { + self.len + } + + pub fn get_data(&self) -> [u8 ;MAXIUM_LEN] { + self.data + } + + pub fn clean_buf(&mut self) { + for x in self.data.as_mut() { + *x = 0 as u8; + } + self.len = 0; + } + + pub fn push_char(&mut self, c: char) { + self.data[self.len] = c as u8; + self.len += 1; + } + + fn compare(&self, other: &Self) -> bool { + self.data.starts_with(&other.data) + } +} + +#[allow(dead_code)] +impl PartialEq for ArrString { + fn eq(&self, other: &Self) -> bool { + self.compare(other) + } +} diff --git a/kernel/src/bsp.rs b/bootloader/src/bsp.rs similarity index 100% rename from kernel/src/bsp.rs rename to bootloader/src/bsp.rs diff --git a/bootloader/src/bsp/device_driver.rs b/bootloader/src/bsp/device_driver.rs new file mode 100644 index 000000000..17243ac8b --- /dev/null +++ b/bootloader/src/bsp/device_driver.rs @@ -0,0 +1,4 @@ +mod bcm; +mod common; + +pub use bcm::*; \ No newline at end of file diff --git a/bootloader/src/bsp/device_driver/bcm.rs b/bootloader/src/bsp/device_driver/bcm.rs new file mode 100644 index 000000000..38d54f84b --- /dev/null +++ b/bootloader/src/bsp/device_driver/bcm.rs @@ -0,0 +1,10 @@ +mod bcm2xxx_gpio; +// mod bcm2xxx_pl011_uart; +mod bcm2xxx_mini_uart; +mod bcm2xxx_mbox; + + +pub use bcm2xxx_gpio::*; +// pub use bcm2xxx_pl011_uart::*; +pub use bcm2xxx_mini_uart::*; +pub use bcm2xxx_mbox::*; \ No newline at end of file diff --git a/bootloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs new file mode 100644 index 000000000..cb71eabf6 --- /dev/null +++ b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! GPIO Driver. + +use crate::{ + bsp::device_driver::common::MMIODerefWrapper, driver, synchronization, + synchronization::NullLock, +}; +use tock_registers::{ + interfaces::{ReadWriteable, Writeable}, + register_bitfields, register_structs, + registers::ReadWrite, +}; + +//-------------------------------------------------------------------------------------------------- +// Private Definitions +//-------------------------------------------------------------------------------------------------- + +// GPIO registers. +// +// Descriptions taken from +// - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +// - https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf +register_bitfields! { + u32, + + /// GPIO Function Select 1 + GPFSEL1 [ + /// Pin 15 + FSEL15 OFFSET(15) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100, // PL011 UART RX + AltFunc5 = 0b010 // PL011 UART RX + ], + + /// Pin 14 + FSEL14 OFFSET(12) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100, // PL011 UART TX + AltFunc5 = 0b010 // PL011 UART RX + ] + ], + + /// GPIO Pull-up/down Register + /// + /// BCM2837 only. + GPPUD [ + /// Controls the actuation of the internal pull-up/down control line to ALL the GPIO pins. + PUD OFFSET(0) NUMBITS(2) [ + Off = 0b00, + PullDown = 0b01, + PullUp = 0b10 + ] + ], + + /// GPIO Pull-up/down Clock Register 0 + /// + /// BCM2837 only. + GPPUDCLK0 [ + /// Pin 15 + PUDCLK15 OFFSET(15) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 14 + PUDCLK14 OFFSET(14) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ] + ], + + /// GPIO Pull-up / Pull-down Register 0 + /// + /// BCM2711 only. + GPIO_PUP_PDN_CNTRL_REG0 [ + /// Pin 15 + GPIO_PUP_PDN_CNTRL15 OFFSET(30) NUMBITS(2) [ + NoResistor = 0b00, + PullUp = 0b01 + ], + + /// Pin 14 + GPIO_PUP_PDN_CNTRL14 OFFSET(28) NUMBITS(2) [ + NoResistor = 0b00, + PullUp = 0b01 + ] + ] +} + +register_structs! { + #[allow(non_snake_case)] + RegisterBlock { + (0x00 => _reserved1), + (0x04 => GPFSEL1: ReadWrite), + (0x08 => _reserved2), + (0x94 => GPPUD: ReadWrite), + (0x98 => GPPUDCLK0: ReadWrite), + (0x9C => _reserved3), + (0xE4 => GPIO_PUP_PDN_CNTRL_REG0: ReadWrite), + (0xE8 => @END), + } +} + +/// Abstraction for the associated MMIO registers. +type Registers = MMIODerefWrapper; + +struct GPIOInner { + registers: Registers, +} + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Representation of the GPIO HW. +pub struct GPIO { + inner: NullLock, +} + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +impl GPIOInner { + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + registers: Registers::new(mmio_start_addr), + } + } + + /// Disable pull-up/down on pins 14 and 15. + fn disable_pud_14_15_bcm2837(&mut self) { + use crate::cpu; + + // Make an educated guess for a good delay value (Sequence described in the BCM2837 + // peripherals PDF). + // + // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz. + // - The Linux 2837 GPIO driver waits 1 µs between the steps. + // + // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs + // would the CPU be clocked at 2 GHz. + const DELAY: usize = 2000; + + self.registers.GPPUD.write(GPPUD::PUD::Off); + cpu::spin_for_cycles(DELAY); + + self.registers + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK15::AssertClock + GPPUDCLK0::PUDCLK14::AssertClock); + cpu::spin_for_cycles(DELAY); + + self.registers.GPPUD.write(GPPUD::PUD::Off); + self.registers.GPPUDCLK0.set(0); + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + #[allow(dead_code)] + pub fn map_pl011_uart(&mut self) { + // Select the UART on pins 14 and 15. + self.registers + .GPFSEL1 + .modify(GPFSEL1::FSEL15::AltFunc0 + GPFSEL1::FSEL14::AltFunc0); + + // Disable pull-up/down on pins 14 and 15. + self.disable_pud_14_15_bcm2837(); + } + + /// Map MINI UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_mini_uart(&mut self) { + // Select the UART on pins 14 and 15. + self.registers + .GPFSEL1 + .modify(GPFSEL1::FSEL15::AltFunc5 + GPFSEL1::FSEL14::AltFunc5); + + // Disable pull-up/down on pins 14 and 15. + self.disable_pud_14_15_bcm2837(); + } +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl GPIO { + pub const COMPATIBLE: &'static str = "BCM GPIO"; + + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + inner: NullLock::new(GPIOInner::new(mmio_start_addr)), + } + } + + /// Concurrency safe version of `GPIOInner.map_pl011_uart()` + #[allow(dead_code)] + pub fn map_pl011_uart(&self) { + self.inner.lock(|inner| inner.map_pl011_uart()) + } + + /// Concurrency safe version of `GPIOInner.map_mini_uart()` + pub fn map_mini_uart(&self) { + self.inner.lock(|inner| inner.map_mini_uart()) + } +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ +use synchronization::interface::Mutex; + +impl driver::interface::DeviceDriver for GPIO { + fn compatible(&self) -> &'static str { + Self::COMPATIBLE + } +} \ No newline at end of file diff --git a/bootloader/src/bsp/device_driver/bcm/bcm2xxx_mbox.rs b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_mbox.rs new file mode 100644 index 000000000..a2051dde4 --- /dev/null +++ b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_mbox.rs @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! MBOX Driver. + +use crate::println; + +use crate::{ + bsp::device_driver::common::MMIODerefWrapper, + driver, mbox, + synchronization::{interface::Mutex, NullLock}, +}; + +use aarch64_cpu::asm; + +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_bitfields, register_structs, + registers::ReadWrite, +}; + +//-------------------------------------------------------------------------------------------------- +// Private Definitions +//-------------------------------------------------------------------------------------------------- + +// MBOX registers + +const CHANNEL_GPU: u32 = 8; + +register_bitfields! { + u32, + + MBOX_READ_REG [ + DATA OFFSET(0) NUMBITS(32) [] + ], + MBOX_PEEK_REG [ + DATA OFFSET(0) NUMBITS(32) [] + ], + MBOX_SENDER_REG [ + DATA OFFSET(0) NUMBITS(32) [] + ], + MBOX_STATUS_REG [ + DATA OFFSET(0) NUMBITS(32) [ + empty = 0x4000_0000, + full = 0x8000_0000 + ] + ], + MBOX_CONFIG_REG [ + DATA OFFSET(0) NUMBITS(32) [] + ], + MBOX_WRITE_REG [ + DATA OFFSET(0) NUMBITS(32) [] + ] +} + +register_structs! { + #[allow(non_snake_case)] + RegisterBlock { + (0x00 => MBOX_READ_REG: ReadWrite), + (0x04 => _reserved1), + (0x10 => MBOX_PEEK_REG: ReadWrite), + (0x14 => MBOX_SENDER_REG: ReadWrite), + (0x18 => MBOX_STATUS_REG: ReadWrite), + (0x1C => MBOX_CONFIG_REG: ReadWrite), + (0x20 => MBOX_WRITE_REG: ReadWrite), + (0x24 => @END), + } +} + +/// Abstraction for the associated MMIO registers. +type Registers = MMIODerefWrapper; + +struct MBOXInner { + registers: Registers, +} + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Representation of the MBOX HW. +pub struct MBOX { + inner: NullLock, +} + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +#[repr(align(64))] +#[derive(Copy, Clone)] +struct MailboxMsg { + buffer: [u32; 8], +} + +impl MBOXInner { + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + registers: Registers::new(mmio_start_addr), + } + } + + pub fn init(&mut self) {} + + fn mailbox_read(&mut self, channel: u32) -> u32 { + loop { + while self + .registers + .MBOX_STATUS_REG + .matches_all(MBOX_STATUS_REG::DATA::empty) + { + asm::nop(); + } + + let data = self.registers.MBOX_READ_REG.get(); + + if (data & 0xF) == channel { + return data; + } + } + } + + fn mailbox_write(&mut self, channel: u32, data: u32) { + while self + .registers + .MBOX_STATUS_REG + .matches_all(MBOX_STATUS_REG::DATA::full) + { + asm::nop(); + } + self.registers.MBOX_WRITE_REG.set(data | channel); + } + + fn mailbox_call(&mut self, mailbox: &mut [u32]) -> bool { + let mailbox_ptr = mailbox.as_mut_ptr() as u32; + if mailbox_ptr & 0xF != 0 { + println!("{:x}", mailbox_ptr); + println!("ptr failed"); + return false; + } + + Self::mailbox_write(self, CHANNEL_GPU, mailbox_ptr); + + if Self::mailbox_read(self, CHANNEL_GPU) != mailbox_ptr | CHANNEL_GPU { + println!("Call failed"); + return false; + } + + true + } + + pub fn get_board_revision(&mut self) -> u32 { + let mut mailbox = MailboxMsg { buffer: [0; 8] }; + mailbox.buffer[0] = 7 * 4; + mailbox.buffer[1] = 0; + mailbox.buffer[2] = 0x0001_0002; + mailbox.buffer[3] = 4; + mailbox.buffer[4] = 0; + mailbox.buffer[5] = 0; + mailbox.buffer[6] = 0x0000_0000; + // let mut mbox = [ + // 7 * 4, // Buffer size in bytes + // 0, // Request/response code + // 0x0001_0002, // Tag: Get board revision + // 4, // Buffer size in bytes + // 0, // Tag request code + // 0, // Value buffer + // 0x0000_0000, // End tag + // ]; + if !Self::mailbox_call(self, &mut mailbox.buffer) { + println!("get board failed"); + return 0; + } + mailbox.buffer[5] + } + + pub fn get_arm_memory(&mut self) -> (u32, u32) { + let mut mailbox = MailboxMsg { buffer: [0; 8] }; + mailbox.buffer[0] = 8 * 4; + mailbox.buffer[1] = 0; + mailbox.buffer[2] = 0x0001_0005; + mailbox.buffer[3] = 8; + mailbox.buffer[4] = 0; + mailbox.buffer[5] = 0; + mailbox.buffer[6] = 0; + mailbox.buffer[7] = 0x0000_0000; + // let mut mbox = [ + // 8 * 4, // Buffer size in bytes + // 0, // Request/response code + // 0x0001_0005, // Tag: Get ARM memory + // 8, // Buffer size in bytes + // 0, // Tag request code + // 0, // Value buffer + // 0, // Value buffer + // 0x0000_0000, // End tag + // ]; + if !Self::mailbox_call(self, &mut mailbox.buffer) { + println!("get mem failed"); + return (0, 0); + } + (mailbox.buffer[5], mailbox.buffer[6]) + } +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl MBOX { + pub const COMPATIBLE: &'static str = "RPI3 MBOX"; + + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + inner: NullLock::new(MBOXInner::new(mmio_start_addr)), + } + } +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ + +impl driver::interface::DeviceDriver for MBOX { + fn compatible(&self) -> &'static str { + Self::COMPATIBLE + } + + unsafe fn init(&self) -> Result<(), &'static str> { + self.inner.lock(|inner| inner.init()); + Ok(()) + } +} + +impl mbox::interface::Boardinfo for MBOX { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn get_arm_memory(&self) -> (u32, u32) { + self.inner.lock(|inner| inner.get_arm_memory()) + } + + fn get_board_revision(&self) -> u32 { + self.inner.lock(|inner| inner.get_board_revision()) + } +} + +impl mbox::interface::All for MBOX {} \ No newline at end of file diff --git a/bootloader/src/bsp/device_driver/bcm/bcm2xxx_mini_uart.rs b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_mini_uart.rs new file mode 100644 index 000000000..40e8d83a3 --- /dev/null +++ b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_mini_uart.rs @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! MINI UART driver. +//! +//! # Resources +//! +//! - +//! - + +use crate::{ + bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization, + synchronization::NullLock, +}; +use aarch64_cpu::asm; +use core::fmt; +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_bitfields, register_structs, + registers::{ReadOnly, ReadWrite}, +}; + +//-------------------------------------------------------------------------------------------------- +// Private Definitions +//-------------------------------------------------------------------------------------------------- + +// MINI UART registers. +// +// Descriptions taken from "PrimeCell UART (MINI) Technical Reference Manual" r1p5. + +register_bitfields! { + u32, + /// Auxiliary peripherals Register + AUXIRQ [ + MINI_UART_IRQ OFFSET(0) NUMBITS(1) [] + ], + + /// Auxiliary enables + AUXENB [ + MINI_UART_ENABLE OFFSET(0) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ] + ], + + /// Mini Uart I/O Data + AUX_MU_IO_REG [ + /// LS 8 bits Baudrate read/write, DLAB=1 + /// Transmit data write, DLAB=0 + /// Receive data read, DLAB=0 + DATA OFFSET(0) NUMBITS(8) [] + ], + + + /// Mini Uart Interrupt Enable + AUX_MU_IER_REG [ + RX OFFSET(0) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ], + + TX OFFSET(1) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ] + ], + + /// Mini Uart Interrupt Identify + AUX_MU_IIR_REG [ + /// This bit is clear whenever an interrupt is pending + INTERRUPT_PENDING OFFSET(0) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ], + + /// On read this register shows the interrupt ID bit + /// 00 : No interrupts + /// 01 : Transmit holding register empty + /// 10 : Receiver holds valid byte + /// 11 : + /// On write: + /// Writing with bit 1 set will clear the receive FIFO + /// Writing with bit 2 set will clear the transmit FIFO + INTERRUPT_ID OFFSET(1) NUMBITS(2) [ + NO_INTERRUPT = 0b00, + TX_HOLD = 0b01, + RX_HOLD = 0b10, + NOT_POSSIBLE = 0b11 + ] + ], + + // Mini Uart Line Control + AUX_MU_LCR_REG [ + DATA_SIZE OFFSET(0) NUMBITS(2) [ + EIGHT_BIT = 0b00, + SEVEN_BIT = 0b11 + ] + ], + + // Mini Uart Modem Control + AUX_MU_MCR_REG [ + RTS OFFSET(1) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ] + ], + + // Mini Uart Line Status + AUX_MU_LSR_REG [ + DATA_READY OFFSET(0) NUMBITS(1) [ + No = 0, + Yes = 1 + ], + TX_EMPTY OFFSET(5) NUMBITS(1) [ + No = 0, + Yes = 1 + ], + TX_IDLE OFFSET(6) NUMBITS(1) [ + No = 0, + Yes = 1 + ], + ], + + /// Mini Uart Extra Control + AUX_MU_CNTL_REG [ + ALL OFFSET(0) NUMBITS(8) + ], + + /// Mini Uart Baudrate + AUX_MU_BAUD_REG [ + BUAD_RATE OFFSET(0) NUMBITS(16) [ + Default = 270 + ] + ] +} + +register_structs! { + #[allow(non_snake_case)] + pub RegisterBlock { + (0x00 => AUXIRQ: ReadOnly), + (0x04 => AUXENB: ReadWrite), + (0x08 => _reserved1), + (0x40 => AUX_MU_IO_REG: ReadWrite), + (0x44 => AUX_MU_IER_REG: ReadWrite), + (0x48 => AUX_MU_IIR_REG: ReadWrite), + (0x4c => AUX_MU_LCR_REG: ReadWrite), + (0x50 => AUX_MU_MCR_REG: ReadWrite), + (0x54 => AUX_MU_LSR_REG: ReadOnly), + (0x58 => _reserved2), + (0x60 => AUX_MU_CNTL_REG: ReadWrite), + (0x64 => _reserved3), + (0x68 => AUX_MU_BAUD_REG: ReadWrite), + (0x6C => @END), + } +} + +/// Abstraction for the associated MMIO registers. +type Registers = MMIODerefWrapper; + +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + +struct MiniUartInner { + registers: Registers, + chars_written: usize, + chars_read: usize, +} + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Representation of the UART. +pub struct MiniUart { + inner: NullLock, +} + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +impl MiniUartInner { + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + registers: Registers::new(mmio_start_addr), + chars_written: 0, + chars_read: 0, + } + } + + /// Set up baud rate and characteristics. + pub fn init(&mut self) { + // Set AUXENB register to enable mini UART. Then mini UART register can be accessed. + self.registers.AUXENB.set(1); + + // Set AUX_MU_CNTL_REG to 0. Disable transmitter and receiver during configuration. + // Set AUX_MU_IER_REG to 0. Disable interrupt because currently you don’t need interrupt. + // Set AUX_MU_LCR_REG to 3. Set the data size to 8 bit. + // Set AUX_MU_MCR_REG to 0. Don’t need auto flow control. + // Set AUX_MU_BAUD to 270. Set baud rate to 115200 + // After booting, the system clock is 250 MHz. + // Set AUX_MU_IIR_REG to 6. + // Set AUX_MU_CNTL_REG to 3. Enable the transmitter and receiver. + self.registers.AUX_MU_CNTL_REG.set(0); + self.registers.AUX_MU_IER_REG.set(0); + self.registers.AUX_MU_LCR_REG.set(3); + self.registers.AUX_MU_MCR_REG.set(0); + self.registers + .AUX_MU_BAUD_REG + .write(AUX_MU_BAUD_REG::BUAD_RATE::Default); + // self.registers.AUX_MU_IIR_REG.set(0xc6); //todo NOT_POSSIBLE + self.registers.AUX_MU_IIR_REG.write(AUX_MU_IIR_REG::INTERRUPT_ID::NOT_POSSIBLE); //todo NOT_POSSIBLE + + self.registers.AUX_MU_CNTL_REG.set(3); + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // wait while input + while self + .registers + .AUX_MU_LSR_REG + .matches_all(AUX_MU_LSR_REG::TX_IDLE::No) + { + asm::nop(); + } + + // Write the character to the buffer. + self.registers.AUX_MU_IO_REG.set(c as u32); + + self.chars_written += 1; + } + + /// Block execution until the last buffered character has been physically put on the TX wire. + fn flush(&self) { + // Spin until the busy bit is cleared. + while self + .registers + .AUX_MU_LSR_REG + .matches_all(AUX_MU_LSR_REG::TX_IDLE::No) + { + cpu::nop(); + } + } + + /// Retrieve a character. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self + .registers + .AUX_MU_LSR_REG + .matches_all(AUX_MU_LSR_REG::DATA_READY::No) + { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self + .registers + .AUX_MU_LSR_REG + .matches_all(AUX_MU_LSR_REG::DATA_READY::No) + { + cpu::nop(); + } + } + + // Otherwise, wait until a char was received. + while self + .registers + .AUX_MU_LSR_REG + .matches_all(AUX_MU_LSR_REG::DATA_READY::No) + { + cpu::nop(); + } + + // Read one character. + let ret = self.registers.AUX_MU_IO_REG.get() as u8 as char; + + // Convert carrige return to newline. + // if ret == '\r' { + // ret = '\n' + // } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for MiniUartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + self.write_char(c); + } + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl MiniUart { + pub const COMPATIBLE: &'static str = "MINI UART"; + + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + inner: NullLock::new(MiniUartInner::new(mmio_start_addr)), + } + } +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ +use synchronization::interface::Mutex; + +impl driver::interface::DeviceDriver for MiniUart { + fn compatible(&self) -> &'static str { + Self::COMPATIBLE + } + + unsafe fn init(&self) -> Result<(), &'static str> { + self.inner.lock(|inner| inner.init()); + + Ok(()) + } +} + +impl console::interface::Write for MiniUart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + self.inner.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write_fmt()` to increase + // readability. + self.inner.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + // Spin until TX FIFO empty is set. + self.inner.lock(|inner| inner.flush()); + } +} + +impl console::interface::Read for MiniUart { + fn read_char(&self) -> char { + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) + } + + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} + } +} + +impl console::interface::Statistics for MiniUart { + fn chars_written(&self) -> usize { + self.inner.lock(|inner| inner.chars_written) + } + + fn chars_read(&self) -> usize { + self.inner.lock(|inner| inner.chars_read) + } +} + +impl console::interface::All for MiniUart {} diff --git a/bootloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 000000000..1edf35938 --- /dev/null +++ b/bootloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! PL011 UART driver. +//! +//! # Resources +//! +//! - +//! - + +use crate::{ + bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization, + synchronization::NullLock, +}; +use core::fmt; +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_bitfields, register_structs, + registers::{ReadOnly, ReadWrite, WriteOnly}, +}; + +//-------------------------------------------------------------------------------------------------- +// Private Definitions +//-------------------------------------------------------------------------------------------------- + +// PL011 UART registers. +// +// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5. +register_bitfields! { + u32, + + /// Flag Register. + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, LCR_H. + /// + /// - If the FIFO is disabled, this bit is set when the transmit holding register is empty. + /// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. + /// - This bit does not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// LCR_H Register. + /// + /// - If the FIFO is disabled, this bit is set when the transmit holding register is full. + /// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// LCR_H Register. + /// + /// - If the FIFO is disabled, this bit is set when the receive holding register is empty. + /// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [], + + /// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains + /// set until the complete byte, including all the stop bits, has been sent from the shift + /// register. + /// + /// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether + /// the UART is enabled or not. + BUSY OFFSET(3) NUMBITS(1) [] + ], + + /// Integer Baud Rate Divisor. + IBRD [ + /// The integer baud rate divisor. + BAUD_DIVINT OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud Rate Divisor. + FBRD [ + /// The fractional baud rate divisor. + BAUD_DIVFRAC OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control Register. + LCR_H [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + #[allow(clippy::enum_variant_names)] + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers. + /// + /// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register. + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for either UART signals or SIR signals depending on the setting of + /// the SIREN bit. When the UART is disabled in the middle of reception, it completes the + /// current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for either UART signals, or SIR signals depending on the + /// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it + /// completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable: + /// + /// 0 = UART is disabled. If the UART is disabled in the middle of transmission or + /// reception, it completes the current character before stopping. + /// + /// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals + /// or SIR signals depending on the setting of the SIREN bit + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// Interrupt Clear Register. + ICR [ + /// Meta field for all pending interrupts. + ALL OFFSET(0) NUMBITS(11) [] + ] +} + +register_structs! { + #[allow(non_snake_case)] + pub RegisterBlock { + (0x00 => DR: ReadWrite), + (0x04 => _reserved1), + (0x18 => FR: ReadOnly), + (0x1c => _reserved2), + (0x24 => IBRD: WriteOnly), + (0x28 => FBRD: WriteOnly), + (0x2c => LCR_H: WriteOnly), + (0x30 => CR: WriteOnly), + (0x34 => _reserved3), + (0x44 => ICR: WriteOnly), + (0x48 => @END), + } +} + +/// Abstraction for the associated MMIO registers. +type Registers = MMIODerefWrapper; + +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + +struct PL011UartInner { + registers: Registers, + chars_written: usize, + chars_read: usize, +} + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Representation of the UART. +pub struct PL011Uart { + inner: NullLock, +} + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +impl PL011UartInner { + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + registers: Registers::new(mmio_start_addr), + chars_written: 0, + chars_read: 0, + } + } + + /// Set up baud rate and characteristics. + /// + /// This results in 8N1 and 921_600 baud. + /// + /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt): + /// `(48_000_000 / 16) / 921_600 = 3.2552083`. + /// + /// This means the integer part is `3` and goes into the `IBRD`. + /// The fractional part is `0.2552083`. + /// + /// `FBRD` calculation according to the PL011 Technical Reference Manual: + /// `INTEGER((0.2552083 * 64) + 0.5) = 16`. + /// + /// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a + /// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`. + /// + /// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`. + pub fn init(&mut self) { + // Execution can arrive here while there are still characters queued in the TX FIFO and + // actively being sent out by the UART hardware. If the UART is turned off in this case, + // those queued characters would be lost. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. + self.registers.CR.set(0); + + // Clear all pending interrupts. + self.registers.ICR.write(ICR::ALL::CLEAR); + + // From the PL011 Technical Reference Manual: + // + // The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is + // updated on a single write strobe generated by a LCR_H write. So, to internally update the + // contents of IBRD or FBRD, a LCR_H write must always be performed at the end. + // + // Set the baud rate, 8N1 and FIFO enabled. + self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3)); + self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16)); + self.registers + .LCR_H + .write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled); + + // Turn the UART on. + self.registers + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Spin while TX FIFO full is set, waiting for an empty slot. + while self.registers.FR.matches_all(FR::TXFF::SET) { + cpu::nop(); + } + + // Write the character to the buffer. + self.registers.DR.set(c as u32); + + self.chars_written += 1; + } + + /// Block execution until the last buffered character has been physically put on the TX wire. + fn flush(&self) { + // Spin until the busy bit is cleared. + while self.registers.FR.matches_all(FR::BUSY::SET) { + cpu::nop(); + } + } + + /// Retrieve a character. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + self.write_char(c); + } + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl PL011Uart { + pub const COMPATIBLE: &'static str = "BCM PL011 UART"; + + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. + pub const unsafe fn new(mmio_start_addr: usize) -> Self { + Self { + inner: NullLock::new(PL011UartInner::new(mmio_start_addr)), + } + } +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ +use synchronization::interface::Mutex; + +impl driver::interface::DeviceDriver for PL011Uart { + fn compatible(&self) -> &'static str { + Self::COMPATIBLE + } + + unsafe fn init(&self) -> Result<(), &'static str> { + self.inner.lock(|inner| inner.init()); + + Ok(()) + } +} + +impl console::interface::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + self.inner.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write_fmt()` to increase + // readability. + self.inner.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + // Spin until TX FIFO empty is set. + self.inner.lock(|inner| inner.flush()); + } +} + +impl console::interface::Read for PL011Uart { + fn read_char(&self) -> char { + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) + } + + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} + } +} + +impl console::interface::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + self.inner.lock(|inner| inner.chars_written) + } + + fn chars_read(&self) -> usize { + self.inner.lock(|inner| inner.chars_read) + } +} + +impl console::interface::All for PL011Uart {} \ No newline at end of file diff --git a/bootloader/src/bsp/device_driver/common.rs b/bootloader/src/bsp/device_driver/common.rs new file mode 100644 index 000000000..cd900024b --- /dev/null +++ b/bootloader/src/bsp/device_driver/common.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2020-2023 Andre Richter + +//! Common device driver code. + +use core::{marker::PhantomData, ops}; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +pub struct MMIODerefWrapper { + start_addr: usize, + phantom: PhantomData T>, +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl MMIODerefWrapper { + /// Create an instance. + pub const unsafe fn new(start_addr: usize) -> Self { + Self { + start_addr, + phantom: PhantomData, + } + } +} + +impl ops::Deref for MMIODerefWrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self.start_addr as *const _) } + } +} \ No newline at end of file diff --git a/bootloader/src/bsp/mbox.rs b/bootloader/src/bsp/mbox.rs new file mode 100644 index 000000000..1e13075a1 --- /dev/null +++ b/bootloader/src/bsp/mbox.rs @@ -0,0 +1,10 @@ +use crate::mbox; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +/// Return a reference to the console. +pub fn mbox() -> &'static dyn mbox::interface::All { + &super::driver::MBOX +} \ No newline at end of file diff --git a/bootloader/src/bsp/rpi3.rs b/bootloader/src/bsp/rpi3.rs new file mode 100644 index 000000000..71a1c355b --- /dev/null +++ b/bootloader/src/bsp/rpi3.rs @@ -0,0 +1,8 @@ +pub mod cpu; +pub mod driver; +pub mod memory; + +#[allow(dead_code)] +pub fn board_name() -> &'static str { + "Raspberry Pi 3" +} diff --git a/bootloader/src/bsp/rpi3/console.rs b/bootloader/src/bsp/rpi3/console.rs new file mode 100644 index 000000000..9d09183da --- /dev/null +++ b/bootloader/src/bsp/rpi3/console.rs @@ -0,0 +1,11 @@ +use crate::console; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +/// Return a reference to the console. +pub fn console() -> &'static dyn console::interface::All { + // &super::driver::PL011_UART + &super::driver::MINI_UART +} \ No newline at end of file diff --git a/bootloader/src/bsp/rpi3/cpu.rs b/bootloader/src/bsp/rpi3/cpu.rs new file mode 100644 index 000000000..fc374a0c9 --- /dev/null +++ b/bootloader/src/bsp/rpi3/cpu.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! BSP Processor code. + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Used by `arch` code to find the early boot core. +#[no_mangle] +#[link_section = ".text._start_arguments"] +pub static BOOT_CORE_ID: u64 = 0; \ No newline at end of file diff --git a/bootloader/src/bsp/rpi3/driver.rs b/bootloader/src/bsp/rpi3/driver.rs new file mode 100644 index 000000000..7bb69e441 --- /dev/null +++ b/bootloader/src/bsp/rpi3/driver.rs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! BSP driver support. + +use super::memory::map::mmio; +use crate::{bsp::device_driver, console, mbox, driver as generic_driver}; +use core::sync::atomic::{AtomicBool, Ordering}; + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static MINI_UART: device_driver::MiniUart = + unsafe { device_driver::MiniUart::new(mmio::MINI_UART_START) }; +// static PL011_UART: device_driver::PL011Uart = +// unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) }; +static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) }; +pub static MBOX: device_driver::MBOX = + unsafe { device_driver::MBOX::new(mmio::MAILBOX_START) }; + // unsafe { device_driver::MBOX::new(mmio::MAILBOX_START) }; + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +/// This must be called only after successful init of the UART driver. +fn post_init_uart() -> Result<(), &'static str> { + // console::register_console(&PL011_UART); + console::register_console(&MINI_UART); + + Ok(()) +} + +/// This must be called only after successful init of the GPIO driver. +fn post_init_gpio() -> Result<(), &'static str> { + // GPIO.map_pl011_uart(); + GPIO.map_mini_uart(); + Ok(()) +} + + +fn post_init_mbox() -> Result<(), &'static str> { + mbox::register_mbox(&MBOX); + Ok(()) +} + +fn driver_uart() -> Result<(), &'static str> { + let uart_descriptor = + // generic_driver::DeviceDriverDescriptor::new(&PL011_UART, Some(post_init_uart)); + generic_driver::DeviceDriverDescriptor::new(&MINI_UART, Some(post_init_uart)); + generic_driver::driver_manager().register_driver(uart_descriptor); + + Ok(()) +} + +fn driver_gpio() -> Result<(), &'static str> { + let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio)); + generic_driver::driver_manager().register_driver(gpio_descriptor); + + Ok(()) +} + +fn driver_mbox() -> Result<(), &'static str> { + let mbox_descriptor = generic_driver::DeviceDriverDescriptor::new(&MBOX, Some(post_init_mbox)); + generic_driver::driver_manager().register_driver(mbox_descriptor); + + Ok(()) +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +/// Initialize the driver subsystem. +/// +/// # Safety +/// +/// See child function calls. +pub unsafe fn init() -> Result<(), &'static str> { + static INIT_DONE: AtomicBool = AtomicBool::new(false); + if INIT_DONE.load(Ordering::Relaxed) { + return Err("Init already done"); + } + + driver_gpio()?; + driver_uart()?; + driver_mbox()?; + + + INIT_DONE.store(true, Ordering::Relaxed); + Ok(()) +} \ No newline at end of file diff --git a/bootloader/src/bsp/rpi3/kernel.ld b/bootloader/src/bsp/rpi3/kernel.ld new file mode 100644 index 000000000..bda4f090f --- /dev/null +++ b/bootloader/src/bsp/rpi3/kernel.ld @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: MIT OR Apache-2.0 + * + * Copyright (c) 2018-2022 Andre Richter + */ + +/* The physical address at which the the kernel binary will be loaded by the Raspberry's firmware */ +__rpi_phys_binary_load_addr = 0x80000; + + +ENTRY(__rpi_phys_binary_load_addr) + +/* Flags: + * 4 == R + * 5 == RX + * 6 == RW + * + * Segments are marked PT_LOAD below so that the ELF file provides virtual and physical addresses. + * It doesn't mean all of them need actually be loaded. + */ +PHDRS +{ + segment_boot_core_stack PT_LOAD FLAGS(6); + segment_code PT_LOAD FLAGS(5); + segment_data PT_LOAD FLAGS(6); +} + +SECTIONS +{ + /* Set the link address to 32 MiB */ + . = 0x2000000; + + /*********************************************************************************************** + * Boot Core Stack + ***********************************************************************************************/ + .boot_core_stack (NOLOAD) : + { + /* ^ */ + /* | stack */ + . += __rpi_phys_binary_load_addr; /* | growth */ + /* | direction */ + __boot_core_stack_end_exclusive = .; /* | */ + } :segment_boot_core_stack + + /*********************************************************************************************** + * Code + RO Data + Global Offset Table + ***********************************************************************************************/ + __binary_nonzero_start = .; + .text : + { + KEEP(*(.text._start)) + *(.text._start_arguments) /* Constants (or statics in Rust speak) read by _start(). */ + *(.text._start_rust) /* The Rust entry point */ + *(.text*) /* Everything else */ + } :segment_code + + .rodata : ALIGN(8) { *(.rodata*) } :segment_code + + /*********************************************************************************************** + * Data + BSS + ***********************************************************************************************/ + .data : { *(.data*) } :segment_data + + /* Fill up to 8 byte, b/c relocating the binary is done in u64 chunks */ + . = ALIGN(8); + __binary_nonzero_end_exclusive = .; + + /* Section is zeroed in pairs of u64. Align start and end to 16 bytes */ + .bss (NOLOAD) : ALIGN(16) + { + __bss_start = .; + *(.bss*); + . = ALIGN(16); + __bss_end_exclusive = .; + } :segment_data + + /*********************************************************************************************** + * Misc + ***********************************************************************************************/ + .got : { *(.got*) } + ASSERT(SIZEOF(.got) == 0, "Relocation support not expected") + + /DISCARD/ : { *(.comment*) } +} \ No newline at end of file diff --git a/bootloader/src/bsp/rpi3/memory.rs b/bootloader/src/bsp/rpi3/memory.rs new file mode 100644 index 000000000..c6a49432f --- /dev/null +++ b/bootloader/src/bsp/rpi3/memory.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! BSP Memory Management. + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// The board's physical memory map. +#[rustfmt::skip] +#[allow(dead_code)] +pub(super) mod map { + pub const BOARD_DEFAULT_LOAD_ADDRESS: usize = 0x8_0000; + + pub const GPIO_OFFSET: usize = 0x0020_0000; + pub const PL011_UART_OFFSET: usize = 0x0020_1000; + pub const MINI_UART_OFFSET: usize = 0x0021_5000; + pub const MAILBOX_OFFSET: usize = 0x0000_B880; + + // There is a VideoCore/ARM MMU translating physical addresses to bus addresses. + // The MMU maps physical address 0x3f000000 to bus address 0x7e000000. + // In your code, you should use physical addresses instead of bus addresses. + // However, the reference uses bus addresses. You should translate them into physical one. + pub mod mmio { + use super::*; + + pub const START: usize = 0x3F00_0000; + pub const GPIO_START: usize = START + GPIO_OFFSET; + pub const MAILBOX_START: usize = START + MAILBOX_OFFSET; + pub const PL011_UART_START: usize = START + PL011_UART_OFFSET; + pub const MINI_UART_START :usize = START + MINI_UART_OFFSET; + + } + +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +/// The address on which the Raspberry firmware loads every binary by default. +#[inline(always)] +pub fn board_default_load_addr() -> *const u64 { + map::BOARD_DEFAULT_LOAD_ADDRESS as _ +} diff --git a/kernel/src/console.rs b/bootloader/src/console.rs similarity index 100% rename from kernel/src/console.rs rename to bootloader/src/console.rs diff --git a/bootloader/src/console/null_console.rs b/bootloader/src/console/null_console.rs new file mode 100644 index 000000000..c6ff3b76f --- /dev/null +++ b/bootloader/src/console/null_console.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 Andre Richter + +//! Null console. + +use super::interface; +use core::fmt; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +pub struct NullConsole; + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +pub static NULL_CONSOLE: NullConsole = NullConsole {}; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl interface::Write for NullConsole { + fn write_char(&self, _c: char) {} + + fn write_fmt(&self, _args: fmt::Arguments) -> fmt::Result { + fmt::Result::Ok(()) + } + + fn flush(&self) {} +} + +impl interface::Read for NullConsole { + fn clear_rx(&self) {} +} + +impl interface::Statistics for NullConsole {} +impl interface::All for NullConsole {} \ No newline at end of file diff --git a/bootloader/src/cpu.rs b/bootloader/src/cpu.rs new file mode 100644 index 000000000..1234294f2 --- /dev/null +++ b/bootloader/src/cpu.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! Architectural processor code. +//! +//! # Orientation +//! +//! Since arch modules are imported into generic modules using the path attribute, the path of this +//! file is: +//! +//! crate::cpu::arch_cpu + +use aarch64_cpu::asm; +pub use asm::nop; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +mod boot; + +//-------------------------------------------------------------------------------------------------- +// Architectural Public Reexports +//-------------------------------------------------------------------------------------------------- + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Pause execution on the core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} \ No newline at end of file diff --git a/bootloader/src/cpu/boot.rs b/bootloader/src/cpu/boot.rs new file mode 100644 index 000000000..477b1e5c6 --- /dev/null +++ b/bootloader/src/cpu/boot.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2021-2023 Andre Richter + +//! Architectural boot code. +//! +//! # Orientation +//! +//! Since arch modules are imported into generic modules using the path attribute, the path of this +//! file is: +//! +//! crate::cpu::boot::arch_boot + +use core::arch::global_asm; + +// Assembly counterpart to this file. +global_asm!( + include_str!("boot.s"), + CONST_CORE_ID_MASK = const 0b11 +); + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +/// The Rust entry of the `kernel` binary. +/// +/// The function is called from the assembly `_start` function. +#[no_mangle] +pub unsafe fn _start_rust() -> ! { + crate::kernel_init() +} \ No newline at end of file diff --git a/bootloader/src/cpu/boot.s b/bootloader/src/cpu/boot.s new file mode 100644 index 000000000..a6da7fc31 --- /dev/null +++ b/bootloader/src/cpu/boot.s @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2021-2022 Andre Richter + +//-------------------------------------------------------------------------------------------------- +// Definitions +//-------------------------------------------------------------------------------------------------- + +// Load the address of a symbol into a register, PC-relative. +// +// The symbol must lie within +/- 4 GiB of the Program Counter. +// +// # Resources +// +// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html +.macro ADR_REL register, symbol + adrp \register, \symbol + add \register, \register, #:lo12:\symbol +.endm + +// Load the address of a symbol into a register, absolute. +// +// # Resources +// +// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html +.macro ADR_ABS register, symbol + movz \register, #:abs_g2:\symbol + movk \register, #:abs_g1_nc:\symbol + movk \register, #:abs_g0_nc:\symbol +.endm + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +.section .text._start + +//------------------------------------------------------------------------------ +// fn _start() +//------------------------------------------------------------------------------ +_start: + // x0 will save dtb address, so backup to 0x60000 + ldr x15, =0x50000 + str x0, [x15] + + // Only proceed on the boot core. Park it otherwise. + mrs x0, MPIDR_EL1 + and x0, x0, {CONST_CORE_ID_MASK} + ldr x1, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs + cmp x0, x1 + b.ne .L_parking_loop + + // If execution reaches here, it is the boot core. + + // Initialize DRAM. + ADR_ABS x0, __bss_start + ADR_ABS x1, __bss_end_exclusive + +.L_bss_init_loop: + cmp x0, x1 + b.eq .L_relocate_binary + stp xzr, xzr, [x0], #16 + b .L_bss_init_loop + + // Next, relocate the binary. +.L_relocate_binary: + ADR_REL x0, __binary_nonzero_start // The address the binary got loaded to. + ADR_ABS x1, __binary_nonzero_start // The address the binary was linked to. + ADR_ABS x2, __binary_nonzero_end_exclusive + +.L_copy_loop: + ldr x3, [x0], #8 + str x3, [x1], #8 + cmp x1, x2 + b.lo .L_copy_loop + + // Prepare the jump to Rust code. + // Set the stack pointer. + ADR_ABS x0, __boot_core_stack_end_exclusive + mov sp, x0 + + // Jump to the relocated Rust code. + ADR_ABS x1, _start_rust + br x1 + + // Infinitely wait for events (aka "park the core"). +.L_parking_loop: + wfe + b .L_parking_loop + +.size _start, . - _start +.type _start, function +.global _start \ No newline at end of file diff --git a/bootloader/src/driver.rs b/bootloader/src/driver.rs new file mode 100644 index 000000000..7ff8f2f3b --- /dev/null +++ b/bootloader/src/driver.rs @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! Driver support. + +use crate::{ + println, + synchronization::{interface::Mutex, NullLock}, +}; + +//-------------------------------------------------------------------------------------------------- +// Private Definitions +//-------------------------------------------------------------------------------------------------- + +const NUM_DRIVERS: usize = 5; + +struct DriverManagerInner { + next_index: usize, + descriptors: [Option; NUM_DRIVERS], +} + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Driver interfaces. +pub mod interface { + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &'static str; + + /// Called by the kernel to bring up the device. + /// + /// # Safety + /// + /// - During init, drivers might do stuff with system-wide impact. + unsafe fn init(&self) -> Result<(), &'static str> { + Ok(()) + } + } +} + +/// Tpye to be used as an optional callback after a driver's init() has run. +pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>; + +/// A descriptor for device drivers. +#[derive(Copy, Clone)] +pub struct DeviceDriverDescriptor { + device_driver: &'static (dyn interface::DeviceDriver + Sync), + post_init_callback: Option, +} + +/// Provides device driver management functions. +pub struct DriverManager { + inner: NullLock, +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static DRIVER_MANAGER: DriverManager = DriverManager::new(); + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +impl DriverManagerInner { + /// Create an instance. + pub const fn new() -> Self { + Self { + next_index: 0, + descriptors: [None; NUM_DRIVERS], + } + } +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl DeviceDriverDescriptor { + /// Create an instance. + pub fn new( + device_driver: &'static (dyn interface::DeviceDriver + Sync), + post_init_callback: Option, + ) -> Self { + Self { + device_driver, + post_init_callback, + } + } +} + +/// Return a reference to the global DriverManager. +pub fn driver_manager() -> &'static DriverManager { + &DRIVER_MANAGER +} + +impl DriverManager { + /// Create an instance. + pub const fn new() -> Self { + Self { + inner: NullLock::new(DriverManagerInner::new()), + } + } + + /// Register a device driver with the kernel. + pub fn register_driver(&self, descriptor: DeviceDriverDescriptor) { + self.inner.lock(|inner| { + inner.descriptors[inner.next_index] = Some(descriptor); + inner.next_index += 1; + }) + } + + /// Helper for iterating over registered drivers. + fn for_each_descriptor<'a>(&'a self, f: impl FnMut(&'a DeviceDriverDescriptor)) { + self.inner.lock(|inner| { + inner + .descriptors + .iter() + .filter_map(|x| x.as_ref()) + .for_each(f) + }) + } + + /// Fully initialize all drivers. + /// + /// # Safety + /// + /// - During init, drivers might do stuff with system-wide impact. + pub unsafe fn init_drivers(&self) { + self.for_each_descriptor(|descriptor| { + // 1. Initialize driver. + if let Err(x) = descriptor.device_driver.init() { + panic!( + "Error initializing driver: {}: {}", + descriptor.device_driver.compatible(), + x + ); + } + + // 2. Call corresponding post init callback. + if let Some(callback) = &descriptor.post_init_callback { + if let Err(x) = callback() { + panic!( + "Error during driver post-init callback: {}: {}", + descriptor.device_driver.compatible(), + x + ); + } + } + }); + } + + /// Enumerate all registered device drivers. + #[allow(dead_code)] + pub fn enumerate(&self) { + let mut i: usize = 1; + self.for_each_descriptor(|descriptor| { + println!(" {}. {}", i, descriptor.device_driver.compatible()); + + i += 1; + }); + } +} \ No newline at end of file diff --git a/bootloader/src/kernel.ld b/bootloader/src/kernel.ld new file mode 100644 index 000000000..4b3e54208 --- /dev/null +++ b/bootloader/src/kernel.ld @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: MIT OR Apache-2.0 + * + * Copyright (c) 2018-2022 Andre Richter + */ + +__rpi_phys_dram_start_addr = 0; + +/* The physical address at which the the kernel binary will be loaded by the Raspberry's firmware */ +__rpi_phys_binary_load_addr = 0x80000; + + +ENTRY(__rpi_phys_binary_load_addr) + +/* Flags: + * 4 == R + * 5 == RX + * 6 == RW + * + * Segments are marked PT_LOAD below so that the ELF file provides virtual and physical addresses. + * It doesn't mean all of them need actually be loaded. + */ +PHDRS +{ + segment_boot_core_stack PT_LOAD FLAGS(6); + segment_code PT_LOAD FLAGS(5); + segment_data PT_LOAD FLAGS(6); +} + +SECTIONS +{ + . = __rpi_phys_dram_start_addr; + + /*********************************************************************************************** + * Boot Core Stack + ***********************************************************************************************/ + .boot_core_stack (NOLOAD) : + { + /* ^ */ + /* | stack */ + . += __rpi_phys_binary_load_addr; /* | growth */ + /* | direction */ + __boot_core_stack_end_exclusive = .; /* | */ + } :segment_boot_core_stack + + /*********************************************************************************************** + * Code + RO Data + Global Offset Table + ***********************************************************************************************/ + .text : + { + KEEP(*(.text._start)) + *(.text._start_arguments) /* Constants (or statics in Rust speak) read by _start(). */ + *(.text._start_rust) /* The Rust entry point */ + *(.text*) /* Everything else */ + } :segment_code + + .rodata : ALIGN(8) { *(.rodata*) } :segment_code + + /*********************************************************************************************** + * Data + BSS + ***********************************************************************************************/ + .data : { *(.data*) } :segment_data + + /* Section is zeroed in pairs of u64. Align start and end to 16 bytes */ + .bss (NOLOAD) : ALIGN(16) + { + __bss_start = .; + *(.bss*); + . = ALIGN(16); + __bss_end_exclusive = .; + } :segment_data + + /*********************************************************************************************** + * Misc + ***********************************************************************************************/ + .got : { *(.got*) } + ASSERT(SIZEOF(.got) == 0, "Relocation support not expected") + + /DISCARD/ : { *(.comment*) } +} \ No newline at end of file diff --git a/bootloader/src/lib.rs b/bootloader/src/lib.rs new file mode 100644 index 000000000..2e7f0d4d7 --- /dev/null +++ b/bootloader/src/lib.rs @@ -0,0 +1 @@ +#![no_std] \ No newline at end of file diff --git a/bootloader/src/main.rs b/bootloader/src/main.rs new file mode 100644 index 000000000..6e20bc5ba --- /dev/null +++ b/bootloader/src/main.rs @@ -0,0 +1,139 @@ +#![feature(asm_const)] +#![feature(format_args_nl)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_main] +#![no_std] + +mod arrsting; +mod bsp; +mod console; +mod cpu; +mod driver; +mod mbox; +mod panic_wait; +mod power; +mod print; +mod shell; +mod synchronization; + +/// Early init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order. +unsafe fn kernel_init() -> ! { + // Initialize the BSP driver subsystem. + if let Err(x) = bsp::driver::init() { + panic!("Error initializing BSP driver subsystem: {}", x); + } + + // Initialize all device drivers. + driver::driver_manager().init_drivers(); + // println! is usable from here on. + + // Transition from unsafe to safe. + kernel_main() +} + +// const PI_LOADER_LOGO: &str = r#" +// ___ _ __ _ +// / _ (_) / / ___ __ _ __| | ___ _ __ +// / /_)/ |/ / / _ \ / _` |/ _` |/ _ \ '__| +// / ___/| / /__| (_) | (_| | (_| | __/ | +// \/ |_\____/\___/ \__,_|\__,_|\___|_| +// "#; + +/// The main function running after the early init. +fn kernel_main() -> ! { + use console::console; + + // println!( + // "[0] {} version {}", + // env!("CARGO_PKG_NAME"), + // env!("CARGO_PKG_VERSION") + // ); + // println!("[1] Booting on: {}", bsp::board_name()); + + // println!("[2] Drivers loaded:"); + // driver::driver_manager().enumerate(); + + // println!("[3] Chars written: {}", console().chars_written()); + // println!("[4] Echoing input now"); + + // println!("{}", PI_LOADER_LOGO); + // println!("BoardName: {}", bsp::board_name()); + // println!("BoardVersion: {:x}", mbox::mbox().get_board_revision()); + // println!( + // "RAM: {} {}", + // mbox::mbox().get_arm_memory().0, + // mbox::mbox().get_arm_memory().1 + // ); + // println!(); + // print!("3"); + // console().flush(); + + // Discard any spurious received characters before starting with the loader protocol. + // console().clear_rx(); + + // loop { + // println!("OK!"); + // } + + // let dtb_addr = 0x50000 as *const u8; + // let dtb_addr = unsafe { core::ptr::read_volatile(dtb_addr as *const u32) }; + + // loop { + // println!("OK!"); + // println!("DTB address: {:#x}", dtb_addr); + // } + + // Read the binary's size. + let mut size: u32 = u32::from(console().read_char() as u8); + size |= u32::from(console().read_char() as u8) << 8; + size |= u32::from(console().read_char() as u8) << 16; + size |= u32::from(console().read_char() as u8) << 24; + + // Trust it's not too big. + // console().write_char('O'); + // console().write_char('K'); + + let kernel_addr: *mut u8 = bsp::memory::board_default_load_addr() as *mut u8; + + // println!("image size: {}", size); + + unsafe { + // Read the kernel byte by byte. + let mut checksum: u16 = 0; + for i in 0..size { + let c = console().read_char(); + checksum = (checksum % 100 + c as u16 % 100) % 100; + + core::ptr::write_volatile(kernel_addr.offset(i as isize), c as u8); + // if i % 1024 == 0{ + // println!("pos:{} data:{} checksum:{}",i,c as u16, checksum); + // } + } + } + // unsafe { + // let kernel = core::slice::from_raw_parts_mut(0x80000 as *mut u8, size as usize); + // for i in 0..size { + // let c = console().read_char(); + // kernel[i as usize] = c as u8; + // println!("pos:{} data:{}",i,c as u32); + // } + // } + + println!("[ML] Loaded! Executing the payload now"); + console().flush(); + + // Use black magic to create a function pointer. + let kernel: fn() -> ! = unsafe { core::mem::transmute(kernel_addr) }; + + // println!("[ML] Try to jump to bootloader"); + // Jump to loaded kernel! + kernel(); + + // shell::start_shell(); +} diff --git a/bootloader/src/mbox.rs b/bootloader/src/mbox.rs new file mode 100644 index 000000000..006947be2 --- /dev/null +++ b/bootloader/src/mbox.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +mod null_mbox; + +use crate::synchronization::{self, NullLock}; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Console interfaces. +#[allow(dead_code)] +pub mod interface { + + pub trait Boardinfo { + fn get_board_revision(&self) -> u32 { + 0 + } + + fn get_arm_memory(&self) -> (u32, u32) { + (0, 0) + } + } + + /// Trait alias for a full-fledged console. + pub trait All: Boardinfo {} +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static CUR_MBOX: NullLock<&'static (dyn interface::All + Sync)> = + NullLock::new(&null_mbox::NULL_MBOX); + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +use synchronization::interface::Mutex; + +/// Register a new console. +pub fn register_mbox(new_mbox: &'static (dyn interface::All + Sync)) { + CUR_MBOX.lock(|con| *con = new_mbox); +} + +/// Return a reference to the currently registered console. +/// +/// This is the global console used by all printing macros. +pub fn mbox() -> &'static dyn interface::All { + CUR_MBOX.lock(|con| *con) +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ \ No newline at end of file diff --git a/bootloader/src/mbox/null_mbox.rs b/bootloader/src/mbox/null_mbox.rs new file mode 100644 index 000000000..3d3677bdd --- /dev/null +++ b/bootloader/src/mbox/null_mbox.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 Andre Richter + +//! Null console. + +use super::interface; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +pub struct NullMBOX; + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +pub static NULL_MBOX: NullMBOX = NullMBOX {}; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl interface::Boardinfo for NullMBOX { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn get_arm_memory(&self) -> (u32, u32) { + (0, 0) + } + + fn get_board_revision(&self) -> u32 { + 0 + } +} + + +impl interface::All for NullMBOX {} \ No newline at end of file diff --git a/bootloader/src/panic_wait.rs b/bootloader/src/panic_wait.rs new file mode 100644 index 000000000..8d4a6b257 --- /dev/null +++ b/bootloader/src/panic_wait.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{cpu, println}; +use core::panic::PanicInfo; + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +/// Stop immediately if called a second time. +/// +/// # Note +/// +/// Using atomics here relieves us from needing to use `unsafe` for the static variable. +/// +/// On `AArch64`, which is the only implemented architecture at the time of writing this, +/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store +/// instructions. They are therefore safe to use even with MMU + caching deactivated. +/// +/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load +/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store +fn panic_prevent_reenter() { + use core::sync::atomic::{AtomicBool, Ordering}; + + // #[cfg(not(target_arch = "aarch64"))] + // compile_error!("Add the target_arch to above's check if the following code is safe to use"); + + static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false); + + if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) { + PANIC_IN_PROGRESS.store(true, Ordering::Relaxed); + + return; + } + + cpu::wait_forever() +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + // Protect against panic infinite loops if any of the following code panics itself. + panic_prevent_reenter(); + + let (location, line, column) = match info.location() { + Some(loc) => (loc.file(), loc.line(), loc.column()), + _ => ("???", 0, 0), + }; + + loop{ + println!( + "Kernel panic!\n\n\ + Panic location:\n File '{}', line {}, column {}\n\n\ + {}", + location, + line, + column, + info.message().unwrap_or(&format_args!("")), + ); +} + + cpu::wait_forever() +} \ No newline at end of file diff --git a/bootloader/src/power.rs b/bootloader/src/power.rs new file mode 100644 index 000000000..695728eab --- /dev/null +++ b/bootloader/src/power.rs @@ -0,0 +1,28 @@ +use core::ptr::write_volatile; + +pub fn reboot() { + reset(100); +} + +const PM_PASSWORD: u32 = 0x5a000000; +const PM_RSTC: u32 = 0x3F10_001C; +const PM_WDOG: u32 = 0x3F10_0024; + +#[allow(dead_code)] +pub fn reset(tick: u32) { + unsafe { + let mut r = PM_PASSWORD | 0x20; + write_volatile(PM_RSTC as *mut u32, r); + r = PM_PASSWORD | tick; + write_volatile(PM_WDOG as *mut u32, r); + } +} + +#[allow(dead_code)] +pub fn cancel_reset() { + unsafe { + let r = PM_PASSWORD | 0; + write_volatile(PM_RSTC as *mut u32, r); + write_volatile(PM_WDOG as *mut u32, r); + } +} \ No newline at end of file diff --git a/bootloader/src/print.rs b/bootloader/src/print.rs new file mode 100644 index 000000000..66e45baed --- /dev/null +++ b/bootloader/src/print.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! Printing. +use crate::console; +use core::fmt; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +#[doc(hidden)] +pub fn _print(args: fmt::Arguments) { + console::console().write_fmt(args).unwrap(); +} + +/// Prints without a newline. +/// +/// Carbon copy from +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from +#[macro_export] +macro_rules! println { + () => ($crate::print!("\r\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args!($($arg)*)); + ($crate::print!("\r\n")); + }) +} \ No newline at end of file diff --git a/bootloader/src/shell.rs b/bootloader/src/shell.rs new file mode 100644 index 000000000..956df19ed --- /dev/null +++ b/bootloader/src/shell.rs @@ -0,0 +1,61 @@ +use crate::{arrsting::ArrString, console::console, mbox, power, print, println}; + +#[allow(dead_code)] +pub fn start_shell() -> !{ + let msg_buf_exceed = "[system] Buf size limit exceed, reset buf"; + + let msg_help = "help\t: print this help menu\r\nhello\t: print Hello World!\r\nreboot\t: reboot the device\r\ninfo\t: show the device info"; + let msg_hello_world = "HelloWorld!"; + let msg_not_found = "Command not found"; + let msg_reboot = "Rebooting..."; + + let arr_help = ArrString::new("help"); + let arr_hello = ArrString::new("hello"); + let arr_reboot = ArrString::new("reboot"); + let arr_info = ArrString::new("info"); + + let mut buf = ArrString::new(""); + + println!("TEST VER 0.0.2\r\n"); + print!("{}\r\n#", msg_help); + + loop { + let c = console().read_char(); + console().write_char(c); + + if c == '\n' { + print!("\r"); + if buf == arr_help { + println!("{}", msg_help); + } else if buf == arr_hello { + println!("{}", msg_hello_world); + } else if buf == arr_reboot { + println!("{}", msg_reboot); + power::reboot(); + } else if buf == arr_info { + println!("BoardVersion: {:x}", mbox::mbox().get_board_revision()); + // println!("BoardVersion: {:x}", bsp::driver::MBOX.get_board_revision()); + println!( + "RAM: {} {}", + mbox::mbox().get_arm_memory().0, + mbox::mbox().get_arm_memory().1 + ); + } else { + println!("{}", msg_not_found); + } + + buf.clean_buf(); + print!("#"); + continue; + + // arrsting::arrstrcmp(buf, help); + } else if buf.get_len() == 1024 { + buf.clean_buf(); + println!("{}", msg_buf_exceed); + print!("#"); + continue; + } else { + buf.push_char(c); + } + } +} diff --git a/bootloader/src/synchronization.rs b/bootloader/src/synchronization.rs new file mode 100644 index 000000000..b87cd46a0 --- /dev/null +++ b/bootloader/src/synchronization.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2020-2023 Andre Richter + +//! Synchronization primitives. +//! +//! # Resources +//! +//! - +//! - +//! - + +use core::cell::UnsafeCell; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Synchronization interfaces. +pub mod interface { + + /// Any object implementing this trait guarantees exclusive access to the data wrapped within + /// the Mutex for the duration of the provided closure. + pub trait Mutex { + /// The type of the data that is wrapped by this mutex. + type Data; + + /// Locks the mutex and grants the closure temporary mutable access to the wrapped data. + fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut Self::Data) -> R) -> R; + } +} + +/// A pseudo-lock for teaching purposes. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access from +/// other cores to the contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +pub struct NullLock +where + T: ?Sized, +{ + data: UnsafeCell, +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +unsafe impl Send for NullLock where T: ?Sized + Send {} +unsafe impl Sync for NullLock where T: ?Sized + Send {} + +impl NullLock { + /// Create an instance. + pub const fn new(data: T) -> Self { + Self { + data: UnsafeCell::new(data), + } + } +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ + +impl interface::Mutex for NullLock { + type Data = T; + + fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + let data = unsafe { &mut *self.data.get() }; + + f(data) + } +} \ No newline at end of file diff --git a/bootloader/test.log b/bootloader/test.log new file mode 100644 index 000000000..c897b2f16 --- /dev/null +++ b/bootloader/test.log @@ -0,0 +1,5 @@ +qemu-system-aarch64 -M raspi3b -kernel bootloader.img \ + -display none -serial null -serial pty \ + -dtb ../resources/bcm2710-rpi-3-b-plus.dtb \ + -initrd ../resources/initramfs.cpio +char device redirected to /dev/ttys019 (label serial1) diff --git a/imageUploader/Makefile b/imageUploader/Makefile new file mode 100644 index 000000000..d5b46df63 --- /dev/null +++ b/imageUploader/Makefile @@ -0,0 +1,6 @@ + +all: ../kernel/kernel8.img + poetry run python ./imageuploader/main.py ../kernel/kernel8.img /dev/tty.usbserial-0001 \ + && screen /dev/tty.usbserial-0001 115200 +#poetry run python $(MAIN) ../kernel/kernel8.img /dev/ttys016 + diff --git a/imageUploader/README.md b/imageUploader/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/imageUploader/imageuploader/__init__.py b/imageUploader/imageuploader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/imageUploader/imageuploader/main.py b/imageUploader/imageuploader/main.py new file mode 100644 index 000000000..ed93d7eeb --- /dev/null +++ b/imageUploader/imageuploader/main.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +import sys +import serial +import time +from pathlib import Path + +def send_file(filename, serial_port): + ser = None + try: + # Open the serial port + ser = serial.Serial(serial_port, baudrate=115200, timeout=1) + print(f"Opened serial port {serial_port}") + + # Read the file content + with open(filename, 'rb') as file: + file_content = file.read() + print(f"Opened file {filename}") + + # Send the size of the file as 32-bit little-endian + file_size = len(file_content) + ser.write(file_size.to_bytes(4, 'little')) + + # Send the file content + byte_counter = 0 + checksum = 0 + for chunk in chunks(file_content, file_size, 1): + + ser.write(chunk) + + checksum = (checksum % 100+ int.from_bytes(chunk, "little") % 100 ) % 100 + # time.sleep(0.01) + # print(f"pos:{byte_counter} data:{int.from_bytes(chunk, "little")} checksum:{checksum}"); + + byte_counter += len(chunk) + + if byte_counter % 256 == 0: + time.sleep(0.02) # Add a delay every 256 bytes + print(f"uploading: {byte_counter}/{len(file_content)}") + # print(f"context: {list(chunk)}") + # print(f"context: {int.from_bytes(chunk, "little") }") + print(f"checksum: {checksum}") + + # overofbound + # checksum = 0 + # for i in range(file_size): + # ser.write(file_content[i]) + # byte_counter += 1; + # checksum = (checksum + file_content[i] ) % 1024 + + # if byte_counter % 1024 == 0: + # print(f"context: {file_content[i]}") + # print(f"uploading: {byte_counter}/{file_size}"); + # print(f"checksum: {checksum}") + # time.sleep(0.05) # Add a delay every 1024 bytes + + + print(f"uploading: {byte_counter}/{len(file_content)}"); + print(f"checksum: {checksum}") + print("File sent successfully.") + + text = ser.readline() + print(text) + + except Exception as e: + print(f"Error: {e}") + finally: + if ser is not None and ser.is_open: + ser.close() + print("Serial port closed.") + +def chunks(lst, size ,n): + """Yield successive n-sized chunks from lst.""" + for i in range(0, len(lst), n): + # print(f"chunk start form {i} to {i+n}") + if i+n > size: + # print("END________") + yield lst[i:size] + else: + yield lst[i:i + n] + +def main(): + if len(sys.argv) != 3: + print("Usage: python sender.py ") + sys.exit(1) + + filename = sys.argv[1] + if not Path(filename).is_file(): + print(f"Error: File '{filename}' not found.") + sys.exit(1) + + serial_port = sys.argv[2] + + send_file(filename, serial_port) + +if __name__ == "__main__": + main() diff --git a/imageUploader/poetry.lock b/imageUploader/poetry.lock new file mode 100644 index 000000000..cc4075ff4 --- /dev/null +++ b/imageUploader/poetry.lock @@ -0,0 +1,30 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "progress" +version = "1.6" +description = "Easy to use progress bars" +optional = false +python-versions = "*" +files = [ + {file = "progress-1.6.tar.gz", hash = "sha256:c9c86e98b5c03fa1fe11e3b67c1feda4788b8d0fe7336c2ff7d5644ccfba34cd"}, +] + +[[package]] +name = "pyserial" +version = "3.5" +description = "Python Serial Port Extension" +optional = false +python-versions = "*" +files = [ + {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, + {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, +] + +[package.extras] +cp2110 = ["hidapi"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "292b01046672f02e0ba676e10e29fb6c817b754d9a8090722f94420b0bbc8003" diff --git a/imageUploader/pyproject.toml b/imageUploader/pyproject.toml new file mode 100644 index 000000000..d77cc6ffd --- /dev/null +++ b/imageUploader/pyproject.toml @@ -0,0 +1,16 @@ +[tool.poetry] +name = "imageuploader" +version = "0.1.0" +description = "" +authors = ["Tsung-Che Lu "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +pyserial = "^3.5" +progress = "^1.6" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/imageUploader/tests/__init__.py b/imageUploader/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/kernel/Makefile b/kernel/Makefile index 1259ecb81..425acbb0e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -102,6 +102,16 @@ $(KERNEL_BIN): $(KERNEL_ELF) $(call color_progress_prefix, "Size") $(call disk_usage_KiB, $(KERNEL_BIN)) + +##------------------------------------------------------------------------------ +## Debug +##------------------------------------------------------------------------------ +debug: + qemu-system-aarch64 -M raspi3b -kernel kernel8.img \ + -display none -serial null -serial stdio \ + -dtb ../resources/bcm2710-rpi-3-b-plus.dtb \ + -initrd ../resources/initramfs.cpio + ##------------------------------------------------------------------------------ ## Clean ##------------------------------------------------------------------------------ diff --git a/kernel/src/arrsting.rs b/kernel/src/arrsting.rs index 93d123059..07cd8e650 100644 --- a/kernel/src/arrsting.rs +++ b/kernel/src/arrsting.rs @@ -22,7 +22,7 @@ impl ArrString { self.len } - pub fn get_data(&self) -> [u8 ;MAXIUM_LEN] { + pub fn get_data(&self) -> [u8; MAXIUM_LEN] { self.data } diff --git a/kernel/src/bsp/device_driver.rs b/kernel/src/bsp/device_driver.rs index 17243ac8b..964f7af7c 100644 --- a/kernel/src/bsp/device_driver.rs +++ b/kernel/src/bsp/device_driver.rs @@ -1,4 +1,4 @@ mod bcm; mod common; -pub use bcm::*; \ No newline at end of file +pub use bcm::*; diff --git a/kernel/src/bsp/device_driver/bcm.rs b/kernel/src/bsp/device_driver/bcm.rs index 38d54f84b..f68a61a0d 100644 --- a/kernel/src/bsp/device_driver/bcm.rs +++ b/kernel/src/bsp/device_driver/bcm.rs @@ -1,10 +1,9 @@ mod bcm2xxx_gpio; // mod bcm2xxx_pl011_uart; -mod bcm2xxx_mini_uart; mod bcm2xxx_mbox; - +mod bcm2xxx_mini_uart; pub use bcm2xxx_gpio::*; // pub use bcm2xxx_pl011_uart::*; +pub use bcm2xxx_mbox::*; pub use bcm2xxx_mini_uart::*; -pub use bcm2xxx_mbox::*; \ No newline at end of file diff --git a/kernel/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs b/kernel/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs index cb71eabf6..9b7ca7997 100644 --- a/kernel/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs +++ b/kernel/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs @@ -233,4 +233,4 @@ impl driver::interface::DeviceDriver for GPIO { fn compatible(&self) -> &'static str { Self::COMPATIBLE } -} \ No newline at end of file +} diff --git a/kernel/src/bsp/device_driver/bcm/bcm2xxx_mbox.rs b/kernel/src/bsp/device_driver/bcm/bcm2xxx_mbox.rs index 9cf1c7d82..7c160185c 100644 --- a/kernel/src/bsp/device_driver/bcm/bcm2xxx_mbox.rs +++ b/kernel/src/bsp/device_driver/bcm/bcm2xxx_mbox.rs @@ -8,7 +8,7 @@ use crate::println; use crate::{ bsp::device_driver::common::MMIODerefWrapper, - driver, + driver, mbox, synchronization::{interface::Mutex, NullLock}, }; @@ -138,7 +138,6 @@ impl MBOXInner { println!("{:x}", mailbox_ptr); println!("ptr failed"); return false; - } Self::mailbox_write(self, CHANNEL_GPU, mailbox_ptr); @@ -153,13 +152,13 @@ impl MBOXInner { pub fn get_board_revision(&mut self) -> u32 { let mut mailbox = MailboxMsg { buffer: [0; 8] }; - mailbox.buffer[0] = 7 * 4; - mailbox.buffer[1] = 0; - mailbox.buffer[2] = 0x0001_0002; - mailbox.buffer[3] = 4; - mailbox.buffer[4] = 0; - mailbox.buffer[5] = 0; - mailbox.buffer[6] = 0x0000_0000; + mailbox.buffer[0] = 7 * 4; + mailbox.buffer[1] = 0; + mailbox.buffer[2] = 0x0001_0002; + mailbox.buffer[3] = 4; + mailbox.buffer[4] = 0; + mailbox.buffer[5] = 0; + mailbox.buffer[6] = 0x0000_0000; // let mut mbox = [ // 7 * 4, // Buffer size in bytes // 0, // Request/response code @@ -178,13 +177,14 @@ impl MBOXInner { pub fn get_arm_memory(&mut self) -> (u32, u32) { let mut mailbox = MailboxMsg { buffer: [0; 8] }; - mailbox.buffer[0] = 8 * 4; - mailbox.buffer[1] = 0; - mailbox.buffer[2] = 0x0001_0005; - mailbox.buffer[3] = 0; - mailbox.buffer[4] = 0; - mailbox.buffer[5] = 0; - mailbox.buffer[6] = 0x0000_0000; + mailbox.buffer[0] = 8 * 4; + mailbox.buffer[1] = 0; + mailbox.buffer[2] = 0x0001_0005; + mailbox.buffer[3] = 8; + mailbox.buffer[4] = 0; + mailbox.buffer[5] = 0; + mailbox.buffer[6] = 0; + mailbox.buffer[7] = 0x0000_0000; // let mut mbox = [ // 8 * 4, // Buffer size in bytes // 0, // Request/response code @@ -199,7 +199,7 @@ impl MBOXInner { println!("get mem failed"); return (0, 0); } - (mailbox.buffer[6], mailbox.buffer[7]) + (mailbox.buffer[5], mailbox.buffer[6]) } } @@ -220,14 +220,6 @@ impl MBOX { inner: NullLock::new(MBOXInner::new(mmio_start_addr)), } } - - pub fn get_board_revision(&self) -> u32 { - self.inner.lock(|inner| inner.get_board_revision()) - } - - pub fn get_arm_memory(&self) -> (u32, u32) { - self.inner.lock(|inner| inner.get_arm_memory()) - } } //------------------------------------------------------------------------------ @@ -244,3 +236,17 @@ impl driver::interface::DeviceDriver for MBOX { Ok(()) } } + +impl mbox::interface::Boardinfo for MBOX { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn get_arm_memory(&self) -> (u32, u32) { + self.inner.lock(|inner| inner.get_arm_memory()) + } + + fn get_board_revision(&self) -> u32 { + self.inner.lock(|inner| inner.get_board_revision()) + } +} + +impl mbox::interface::All for MBOX {} diff --git a/kernel/src/bsp/device_driver/bcm/bcm2xxx_mini_uart.rs b/kernel/src/bsp/device_driver/bcm/bcm2xxx_mini_uart.rs index 0eb99c3f5..43a5b6187 100644 --- a/kernel/src/bsp/device_driver/bcm/bcm2xxx_mini_uart.rs +++ b/kernel/src/bsp/device_driver/bcm/bcm2xxx_mini_uart.rs @@ -218,7 +218,9 @@ impl MiniUartInner { .AUX_MU_BAUD_REG .write(AUX_MU_BAUD_REG::BUAD_RATE::Default); // self.registers.AUX_MU_IIR_REG.set(0xc6); //todo NOT_POSSIBLE - self.registers.AUX_MU_IIR_REG.write(AUX_MU_IIR_REG::INTERRUPT_ID::NOT_POSSIBLE); //todo NOT_POSSIBLE + self.registers + .AUX_MU_IIR_REG + .write(AUX_MU_IIR_REG::INTERRUPT_ID::NOT_POSSIBLE); //todo NOT_POSSIBLE self.registers.AUX_MU_CNTL_REG.set(3); } @@ -289,7 +291,7 @@ impl MiniUartInner { // Convert carrige return to newline. if ret == '\r' { - ret = '\n' + ret = '\n'; } // Update statistics. diff --git a/kernel/src/bsp/device_driver/common.rs b/kernel/src/bsp/device_driver/common.rs index cd900024b..dfe7d8ef3 100644 --- a/kernel/src/bsp/device_driver/common.rs +++ b/kernel/src/bsp/device_driver/common.rs @@ -35,4 +35,4 @@ impl ops::Deref for MMIODerefWrapper { fn deref(&self) -> &Self::Target { unsafe { &*(self.start_addr as *const _) } } -} \ No newline at end of file +} diff --git a/kernel/src/bsp/mbox.rs b/kernel/src/bsp/mbox.rs new file mode 100644 index 000000000..1e13075a1 --- /dev/null +++ b/kernel/src/bsp/mbox.rs @@ -0,0 +1,10 @@ +use crate::mbox; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +/// Return a reference to the console. +pub fn mbox() -> &'static dyn mbox::interface::All { + &super::driver::MBOX +} \ No newline at end of file diff --git a/kernel/src/bsp/mod.rs b/kernel/src/bsp/mod.rs new file mode 100644 index 000000000..d6e8cdc8c --- /dev/null +++ b/kernel/src/bsp/mod.rs @@ -0,0 +1,5 @@ +mod device_driver; + +mod rpi3; + +pub use rpi3::*; diff --git a/kernel/src/bsp/rpi3/cpu.rs b/kernel/src/bsp/rpi3/cpu.rs index fc374a0c9..65cf5abbe 100644 --- a/kernel/src/bsp/rpi3/cpu.rs +++ b/kernel/src/bsp/rpi3/cpu.rs @@ -11,4 +11,4 @@ /// Used by `arch` code to find the early boot core. #[no_mangle] #[link_section = ".text._start_arguments"] -pub static BOOT_CORE_ID: u64 = 0; \ No newline at end of file +pub static BOOT_CORE_ID: u64 = 0; diff --git a/kernel/src/bsp/rpi3/driver.rs b/kernel/src/bsp/rpi3/driver.rs index 238f29816..c7dbe64fc 100644 --- a/kernel/src/bsp/rpi3/driver.rs +++ b/kernel/src/bsp/rpi3/driver.rs @@ -5,7 +5,7 @@ //! BSP driver support. use super::memory::map::mmio; -use crate::{bsp::device_driver, console, driver as generic_driver}; +use crate::{bsp::device_driver, console, driver as generic_driver, mbox}; use core::sync::atomic::{AtomicBool, Ordering}; //-------------------------------------------------------------------------------------------------- @@ -17,8 +17,8 @@ static MINI_UART: device_driver::MiniUart = // static PL011_UART: device_driver::PL011Uart = // unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) }; static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) }; -pub static MBOX: device_driver::MBOX = - unsafe { device_driver::MBOX::new(mmio::MAILBOX_START) }; +pub static MBOX: device_driver::MBOX = unsafe { device_driver::MBOX::new(mmio::MAILBOX_START) }; +// unsafe { device_driver::MBOX::new(mmio::MAILBOX_START) }; //-------------------------------------------------------------------------------------------------- // Private Code @@ -39,10 +39,10 @@ fn post_init_gpio() -> Result<(), &'static str> { Ok(()) } - -// fn post_init_mbox() -> Result<(), &'static str> { -// Ok(()) -// } +fn post_init_mbox() -> Result<(), &'static str> { + mbox::register_mbox(&MBOX); + Ok(()) +} fn driver_uart() -> Result<(), &'static str> { let uart_descriptor = @@ -60,12 +60,12 @@ fn driver_gpio() -> Result<(), &'static str> { Ok(()) } -// fn driver_mbox() -> Result<(), &'static str> { -// let mbox_descriptor = generic_driver::DeviceDriverDescriptor::new(&MBOX, Some(post_init_mbox)); -// generic_driver::driver_manager().register_driver(mbox_descriptor); +fn driver_mbox() -> Result<(), &'static str> { + let mbox_descriptor = generic_driver::DeviceDriverDescriptor::new(&MBOX, Some(post_init_mbox)); + generic_driver::driver_manager().register_driver(mbox_descriptor); -// Ok(()) -// } + Ok(()) +} //-------------------------------------------------------------------------------------------------- // Public Code @@ -81,12 +81,11 @@ pub unsafe fn init() -> Result<(), &'static str> { if INIT_DONE.load(Ordering::Relaxed) { return Err("Init already done"); } - + driver_gpio()?; driver_uart()?; - // driver_mbox()?; - + driver_mbox()?; INIT_DONE.store(true, Ordering::Relaxed); Ok(()) -} \ No newline at end of file +} diff --git a/kernel/src/bsp/rpi3/kernel.ld b/kernel/src/bsp/rpi3/kernel.ld index 4b3e54208..4caa2f49e 100644 --- a/kernel/src/bsp/rpi3/kernel.ld +++ b/kernel/src/bsp/rpi3/kernel.ld @@ -67,6 +67,9 @@ SECTIONS *(.bss*); . = ALIGN(16); __bss_end_exclusive = .; + __dtb_address = .; + . += 16; + . = ALIGN(16); } :segment_data /*********************************************************************************************** diff --git a/kernel/src/bsp/rpi3/memory.rs b/kernel/src/bsp/rpi3/memory.rs index 041976de3..e042df3a9 100644 --- a/kernel/src/bsp/rpi3/memory.rs +++ b/kernel/src/bsp/rpi3/memory.rs @@ -10,7 +10,7 @@ /// The board's physical memory map. #[rustfmt::skip] -pub(super) mod map { +pub(crate) mod map { pub const GPIO_OFFSET: usize = 0x0020_0000; // pub const PL011_UART_OFFSET: usize = 0x0020_1000; @@ -28,8 +28,20 @@ pub(super) mod map { pub const GPIO_START: usize = START + GPIO_OFFSET; pub const MAILBOX_START: usize = START + MAILBOX_OFFSET; // pub const PL011_UART_START: usize = START + UART_OFFSET; - pub const MINI_UART_START :usize = START + MINI_UART_OFFSET; + pub const MINI_UART_START: usize = START + MINI_UART_OFFSET; } + /* + After rpi3 is booted, some physical memory is already in use. For example, + there are already spin tables for multicore boot(0x0000 - 0x1000), f + latten device tree, initramfs, and your kernel image in the physical memory. + Your memory allocator should not allocate these memory blocks if you still need to use them. + */ + pub mod sdram { + + pub const RAM_START: usize = 0x1000_0000; + pub const RAM_END: usize = 0x8000_0000; + } + } diff --git a/kernel/src/console/mod.rs b/kernel/src/console/mod.rs new file mode 100644 index 000000000..6370ac24d --- /dev/null +++ b/kernel/src/console/mod.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +//! System console. + +mod null_console; + +use crate::synchronization::{self, NullLock}; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Console interfaces. +#[allow(dead_code)] +pub mod interface { + use core::fmt; + + /// Console write functions. + pub trait Write { + /// Write a single character. + fn write_char(&self, c: char); + + /// Write a Rust format string. + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block until the last buffered character has been physically put on the TX wire. + fn flush(&self); + } + + /// Console read functions. + pub trait Read { + /// Read a single character. + fn read_char(&self) -> char { + ' ' + } + + /// Clear RX buffers, if any. + fn clear_rx(&self); + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All: Write + Read + Statistics {} +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> = + NullLock::new(&null_console::NULL_CONSOLE); + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +use synchronization::interface::Mutex; + +/// Register a new console. +pub fn register_console(new_console: &'static (dyn interface::All + Sync)) { + CUR_CONSOLE.lock(|con| *con = new_console); +} + +/// Return a reference to the currently registered console. +/// +/// This is the global console used by all printing macros. +pub fn console() -> &'static dyn interface::All { + CUR_CONSOLE.lock(|con| *con) +} diff --git a/kernel/src/console/null_console.rs b/kernel/src/console/null_console.rs index c6ff3b76f..e92a022b6 100644 --- a/kernel/src/console/null_console.rs +++ b/kernel/src/console/null_console.rs @@ -38,4 +38,4 @@ impl interface::Read for NullConsole { } impl interface::Statistics for NullConsole {} -impl interface::All for NullConsole {} \ No newline at end of file +impl interface::All for NullConsole {} diff --git a/kernel/src/cpu.rs b/kernel/src/cpu.rs index 1234294f2..884372a05 100644 --- a/kernel/src/cpu.rs +++ b/kernel/src/cpu.rs @@ -36,4 +36,4 @@ pub fn wait_forever() -> ! { loop { asm::wfe() } -} \ No newline at end of file +} diff --git a/kernel/src/cpu/boot.rs b/kernel/src/cpu/boot.rs index 477b1e5c6..2a6c46492 100644 --- a/kernel/src/cpu/boot.rs +++ b/kernel/src/cpu/boot.rs @@ -29,4 +29,4 @@ global_asm!( #[no_mangle] pub unsafe fn _start_rust() -> ! { crate::kernel_init() -} \ No newline at end of file +} diff --git a/kernel/src/cpu/boot.s b/kernel/src/cpu/boot.s index 9696e86b4..565e08c06 100644 --- a/kernel/src/cpu/boot.s +++ b/kernel/src/cpu/boot.s @@ -18,6 +18,18 @@ add \register, \register, #:lo12:\symbol .endm +// Load the address of a symbol into a register, absolute. +// +// # Resources +// +// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html +.macro ADR_ABS register, symbol + movz \register, #:abs_g3:\symbol + movk \register, #:abs_g2_nc:\symbol + movk \register, #:abs_g1_nc:\symbol + movk \register, #:abs_g0_nc:\symbol +.endm + //-------------------------------------------------------------------------------------------------- // Public Code //-------------------------------------------------------------------------------------------------- @@ -27,6 +39,14 @@ // fn _start() //------------------------------------------------------------------------------ _start: + // ldr x1, 0x50000 + // str x0, [x1] + + // load address 0x60000's value and save it into __dtb_address + ADR_ABS x2, __dtb_address + ldr x1, =0x50000 + str x1, [x2] + // Only proceed on the boot core. Park it otherwise. mrs x0, MPIDR_EL1 and x0, x0, {CONST_CORE_ID_MASK} diff --git a/kernel/src/device_tree/dt.rs b/kernel/src/device_tree/dt.rs new file mode 100644 index 000000000..f492accbd --- /dev/null +++ b/kernel/src/device_tree/dt.rs @@ -0,0 +1,165 @@ +extern crate alloc; + +use super::dt_prop::Property; +use super::dt_string::load_string; +use super::dt_string::DtString; +use alloc::string::String; +use alloc::vec::Vec; + +#[derive(Debug)] +pub struct Dt { + name: String, + properties: Vec, + children: Vec
, + length: u32, +} + +#[allow(unused_assignments)] +impl Dt { + pub fn load(dt_struct_addr: u32, dt_string: &DtString) -> Dt { + let mut dt = Dt { + name: String::new(), + properties: Vec::new(), + children: Vec::new(), + length: 0, + }; + + //println!("[Device Tree Dt Root] dt_struct_addr: {:#x}", dt_struct_addr); + + let dt_struct_begin = unsafe { core::ptr::read_volatile(dt_struct_addr as *const u32) }; + // let dt_struct_begin = unsafe { &*(dt_struct_addr as *const u32) }; + // let dt_struct_begin = dt_struct_addr; + + // check fdt struct start with begin + if dt_struct_begin.swap_bytes() != FdtTag::FdtBeginNode as u32 { + //println!("[Device Tree Dt Root] FdtBeginNode Error, value {:#x}, expected: {:#x}", dt_struct_begin, MAGIC_NUMBER); + } else { + //println!("[Device Tree Dt Root] FdtBeginNode OK!"); + } + let struct_root_addr: u32 = dt_struct_addr + 4; + + // create root Device tree node + dt.name = load_string(struct_root_addr); + //println!("[Device Tree Dt Root] struct_root_addr: {:#x}, Root name: {}",struct_root_addr , dt.name); + + // get the next tag addr + // add nullterm + let mut index_addr = struct_root_addr + dt.name.len() as u32 + 1; + index_addr = align_up(index_addr,4); + // let mut index_addr = align_addr(struct_root_addr + dt.name.len() as u32 + 1, 4) + 4; + //println!("[Device Tree Dt Root] index_addr: {:#x}", index_addr); + + // let mut counter:u32 = 0; + + loop { + let tag = unsafe { core::ptr::read_volatile(index_addr as *const u32) }; + let tag = tag.swap_bytes(); + + //println!("[Device Tree Dt] index_addr: {:#x} FdtTag: {:#x} counter: {}", index_addr, tag, counter); + // counter+=1; + + let tag = FdtTag::try_from(tag).unwrap(); + + match tag { + // 0x1 + FdtTag::FdtBeginNode => { + //println!("[Device Tree Dt FdtBeginNode]"); + let child = Dt::load(index_addr, dt_string); + index_addr += child.length; + dt.children.push(child); + } + // 0x2 + FdtTag::FdtEndNode => { + //println!("[Device Tree Dt FdtEndNode]"); + index_addr += 4; + break; + } + // 0x3 + FdtTag::FdtProp => { + index_addr += 4; + //println!("[Device Tree Dt FdtProp] index_addr: {:#x}",index_addr); + let property = Property::load(index_addr, dt_string); + index_addr += property.get_length(); + // index_addr += 8; + //println!("[Device Tree Dt FdtProp] property name: {}, propLen: {:#x}, index_addr: {:#x}",property.name, property.get_length(), index_addr); + dt.properties.push(property); + index_addr = align_up(index_addr,4); + } + // 0x4 + FdtTag::FdtNop => { + //println!("[Device Tree Dt FdtNop]"); + index_addr += 4; + continue; + } + // 0x5 + FdtTag::FdtEnd => { + //println!("[Device Tree Dt FdtEnd]"); + // index_addr; + break; + } + } + } + + dt.length = index_addr - dt_struct_addr; + + // return + dt + } + + pub fn get_prop(&self, name: &str) -> Option<&Property> { + if let Some(prop) = self.properties.iter().find(|prop| prop.name == name) { + return Some(prop); + } else { + for child in self.children.iter() { + if let Some(prop) = child.get_prop(name) { + return Some(prop); + } + } + } + None + } +} + +#[repr(u32)] +pub enum FdtTag { + FdtBeginNode = 0x1, // start name full name + FdtEndNode = 0x2, // end + FdtProp = 0x3, + FdtNop = 0x4, + FdtEnd = 0x9, +} + +impl TryFrom for FdtTag { + type Error = &'static str; + + fn try_from(value: u32) -> Result { + match value { + 0x1 => Ok(FdtTag::FdtBeginNode), + 0x2 => Ok(FdtTag::FdtEndNode), + 0x3 => Ok(FdtTag::FdtProp), + 0x4 => Ok(FdtTag::FdtNop), + 0x9 => Ok(FdtTag::FdtEnd), + _ => Err("Invalid Token"), + } + } +} + + +fn align_up(addr: u32, align: u32) -> u32 { + match addr % align { + 0 => addr, + remainder => addr + align - remainder, + } +} + + +// align the addr to n bytes +// fn align_addr(addr: u32, n: u32) -> u32 { +// // (index_addr + 3) & !3; +// let offset = n - (addr % n); +// if offset == n { +// addr +// } else { +// addr + offset +// } +// } diff --git a/kernel/src/device_tree/dt_header.rs b/kernel/src/device_tree/dt_header.rs new file mode 100644 index 000000000..14ae5dfb8 --- /dev/null +++ b/kernel/src/device_tree/dt_header.rs @@ -0,0 +1,102 @@ +// https://github.com/repnop/fdt/tree/masterfv +// https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html +// https://www.wowotech.net/device_model/dt-code-file-struct-parse.html +// flatten device tree + +const MAGIC_NUMBER: u32 = 0xd00dfeed; + +/// Possible errors when attempting to create an `Fdt` +// #[derive(Debug, Clone, Copy, PartialEq)] +// pub enum FdtError { +// /// The FDT had an invalid magic value +// BadMagic, +// /// The given pointer was null +// BadPtr, +// /// The slice passed in was too small to fit the given total size of the FDT +// /// structure +// BufferTooSmall, +// } + +/// A flattened devicetree located somewhere in memory +/// +/// Note on `Debug` impl: by default the `Debug` impl of this struct will not +/// print any useful information, if you would like a best-effort tree print +/// which looks similar to `dtc`'s output, enable the `pretty-printing` feature +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FdtHeader { + magic: u32, + totalsize: u32, + off_dt_struct: u32, + off_dt_strings: u32, + off_mem_rsvmap: u32, + version: u32, + last_comp_version: u32, + boot_cpuid_phys: u32, + size_dt_strings: u32, + size_dt_struct: u32, +} + +impl FdtHeader { + pub fn valid_magic(&self) -> bool { + // magic number + self.magic == MAGIC_NUMBER + } + + pub fn get_off_dt_struct(&self) -> u32 { + self.off_dt_struct + } + + pub fn get_off_dt_strings(&self) -> u32 { + self.off_dt_strings + } + + // pub fn get_size_dt_struct(&self) -> u32 { + // self.size_dt_struct + // } + + // pub fn get_size_dt_strings(&self) -> u32 { + // self.size_dt_strings + // } + + // pub fn get_struct_range(&self) -> core::ops::Range { + // let start = self.off_dt_struct as usize; + // let end = start + self.size_dt_struct as usize; + + // start..end + // } + + // pub fn get_strings_range(&self) -> core::ops::Range { + // let start = self.off_dt_strings as usize; + // let end = start + self.size_dt_strings as usize; + + // start..end + // } + + pub fn load(dtb_addr: u32) -> FdtHeader { + let header_ptr = unsafe { &*(dtb_addr as *const FdtHeader) }; + // cause device tree use big-endian format, + // so we need to use swap_bytes to change to little-endian + // println!("[fdt_header] Load magic: {:#x}", header_ptr.magic.swap_bytes()); + // println!("[fdt_header] Load totalsize: {:#x}", header_ptr.totalsize.swap_bytes()); + // println!("[fdt_header] Load off_dt_struct: {:#x}", header_ptr.off_dt_struct.swap_bytes()); + // println!("[fdt_header] Load off_dt_strings: {:#x}", header_ptr.off_dt_strings.swap_bytes()); + // println!("[fdt_header] Load version: {:#x}", header_ptr.version.swap_bytes()); + // println!("[fdt_header] Load last_comp_version: {:#x}", header_ptr.last_comp_version.swap_bytes()); + // println!("[fdt_header] Load boot_cpuid_phys: {:#x}", header_ptr.boot_cpuid_phys.swap_bytes()); + // println!("[fdt_header] Load size_dt_strings: {:#x}", header_ptr.size_dt_strings.swap_bytes()); + // println!("[fdt_header] Load size_dt_struct: {:#x}", header_ptr.size_dt_struct.swap_bytes()); + FdtHeader { + magic: header_ptr.magic.swap_bytes(), + totalsize: header_ptr.totalsize.swap_bytes(), + off_dt_struct: header_ptr.off_dt_struct.swap_bytes(), + off_dt_strings: header_ptr.off_dt_strings.swap_bytes(), + off_mem_rsvmap: header_ptr.off_mem_rsvmap.swap_bytes(), + version: header_ptr.version.swap_bytes(), + last_comp_version: header_ptr.last_comp_version.swap_bytes(), + boot_cpuid_phys: header_ptr.boot_cpuid_phys.swap_bytes(), + size_dt_strings: header_ptr.size_dt_strings.swap_bytes(), + size_dt_struct: header_ptr.size_dt_struct.swap_bytes(), + } + } +} diff --git a/kernel/src/device_tree/dt_prop.rs b/kernel/src/device_tree/dt_prop.rs new file mode 100644 index 000000000..4ad93e91c --- /dev/null +++ b/kernel/src/device_tree/dt_prop.rs @@ -0,0 +1,81 @@ +extern crate alloc; + +use super::dt_string::DtString; +use alloc::string::String; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum PropValue { + Integer(u32), + String(String), +} + +struct PropHeader { + length: u32, + nameoff: u32, +} + +impl PropHeader { + fn load(addr: u32) -> PropHeader { + let header = unsafe { &*(addr as *const PropHeader) }; + PropHeader { + length: header.length.swap_bytes(), + nameoff: header.nameoff.swap_bytes(), + } + } +} + +#[derive(Debug)] +pub struct Property { + pub length: u32, + pub name: String, + pub value: PropValue, +} + +impl Property { + pub fn load(property_addr: u32, strings: &DtString) -> Property { + let header = PropHeader::load(property_addr); + // println!( + // "[Device Tree Prop] Prop Header Loaded ,length: {}, nameof: {}", + // header.length, + // header.nameoff + // ); + let name: String = strings.get(header.nameoff); + //println!("[Device Tree Prop] String Loaded"); + let value = match header.length { + 4 | 8 => { + //println!("[Device Tree Prop] Int property_addr: {:#x}", property_addr + 8); + let value = unsafe { core::ptr::read_volatile((property_addr + 8) as *const u32) } + .swap_bytes(); + PropValue::Integer(value) + } + _ => { + //println!("[Device Tree Prop] String property_addr: {:#x}", property_addr + 8); + let value = load_data(property_addr + 8, header.length); + PropValue::String(value) + } + }; + Property { + length: header.length + 8, + name, + value, + } + } + + pub fn get_length(&self) -> u32 { + self.length + } +} + +// get string end with '\0' +pub fn load_data(base_address: u32, len: u32) -> String { + let mut addr = base_address; + let mut string = String::new(); + for _counter in 0..len { + let c = unsafe { core::ptr::read_volatile(addr as *const u8) }; + string.push(c as char); + addr += 1; + } + //println!("[Device Tree load_data] String: {} Addr: {:#x}", string ,addr); + string +} diff --git a/kernel/src/device_tree/dt_string.rs b/kernel/src/device_tree/dt_string.rs new file mode 100644 index 000000000..52d7b34a3 --- /dev/null +++ b/kernel/src/device_tree/dt_string.rs @@ -0,0 +1,46 @@ +extern crate alloc; +use alloc::string::String; + +pub struct DtString { + base_address: u32, +} + +impl DtString { + pub fn load(base_address: u32) -> DtString { + DtString { base_address } + } + + // get string end with '\0' + pub fn get(&self, offset: u32) -> String { + let mut addr = self.base_address + offset; + let mut string = String::new(); + //println!("[Device Tree Dtstring] String: {} Addr: {:#x}", string ,addr); + loop { + let c = unsafe { core::ptr::read_volatile(addr as *const u8) }; + if c == 0 { + break; + } + string.push(c as char); + addr += 1; + } + //println!("[Device Tree Dtstring] String: {} Addr: {:#x}", string ,addr); + string + } +} + +// get string end with '\0' +pub fn load_string(base_address: u32) -> String { + let mut addr = base_address; + let mut string = String::new(); + //println!("[Device Tree load_string] String: {} Addr: {:#x}", string ,addr); + loop { + let c = unsafe { core::ptr::read_volatile(addr as *const u8) }; + if c == 0 { + break; + } + string.push(c as char); + addr += 1; + } + //println!("[Device Tree load_string] String: {} Addr: {:#x}", string ,addr); + string +} diff --git a/kernel/src/device_tree/dt_struct.rs b/kernel/src/device_tree/dt_struct.rs new file mode 100644 index 000000000..84f182c6b --- /dev/null +++ b/kernel/src/device_tree/dt_struct.rs @@ -0,0 +1,15 @@ +// #[repr(C)] +// #[derive(Clone, Copy)] +// pub struct FdtNodeHeader<'a> { +// name: &'a [u8], +// tag: u32, +// } + +// #[repr(C)] +// #[derive(Clone, Copy)] +// pub struct FdtProperty<'a> { +// tag: u32, +// len: u32, +// nameoff: u32, +// data: &'a [u8], // Flexible array member not directly representable in Rust +// } diff --git a/kernel/src/device_tree/mod.rs b/kernel/src/device_tree/mod.rs new file mode 100644 index 000000000..cb0b916be --- /dev/null +++ b/kernel/src/device_tree/mod.rs @@ -0,0 +1,76 @@ +use crate::println; + +pub mod dt; +mod dt_header; +mod dt_prop; +mod dt_string; +mod dt_struct; + +// use special memory address to pass x0 from bootloader to kernel +extern "C" { + static __dtb_address: u32; +} + +fn get_dtb_address() -> u32 { + // let ptr_dtb_address = unsafe { __dtb_address as *const u32 }; + + let dtb_address = unsafe { core::ptr::read_volatile(__dtb_address as *const u32) }; + + // let dtb_address = unsafe { *ptr_dtb_address as *const u32 }; + + // loop{ + // println!("[Device Tree] Value of ptr_dtb_address: {:#x}", dtb_address as u32); + // } + // let ptr_dtb_magic = unsafe { *dtb_address as *const u32 }; + // let dtb_magic = unsafe { *ptr_dtb_magic }; + + //println!("[Device Tree] Value of __dtb_address: {:#x}", unsafe {__dtb_address }); + //println!("[Device Tree] Value of ptr_dtb_address: {:#x}", ptr_dtb_address as u32); + //println!("[Device Tree] Value of dtb_address: {:#x}", dtb_address as u32); + //println!("[Device Tree] Value of dtb_magic: {:#x}", dtb_magic.swap_bytes()); + + // checking dtb address has valid magic value + // if dtb_magic.swap_bytes() != 0xd00dfeed { + // loop{ + // println!("[Device Tree] invalid dtb address"); + // } + // //println!("[Device Tree] invalid dtb address"); + // } + + // unsafe{*dtb_address} + dtb_address as u32 +} + +pub fn load_dtb() -> dt::Dt { + let dtb_address = get_dtb_address(); + // println!("[Device Tree] dtb_address: {:#x}", dtb_address); + + let fdt_header = dt_header::FdtHeader::load(dtb_address); + + if fdt_header.valid_magic() == false { + println!("[Device Tree] fdtHeader magic non valid"); + } + + let strings_addr = dtb_address + fdt_header.get_off_dt_strings(); + //println!("[Device Tree header] off_dt_strings: {:#x}",strings_addr); + let dt_strings = dt_string::DtString::load(strings_addr); + + let dt_struct_addr = dtb_address + fdt_header.get_off_dt_struct(); + //println!("[Device Tree] dt_struct_addr: {:#x}",dt_struct_addr); + + let dt = dt::Dt::load(dt_struct_addr, &dt_strings); + + dt +} + +pub fn get_initrd_start() -> Option { + let dt = load_dtb(); + let node = dt.get_prop("linux,initrd-start"); + match node { + Some(node) => match node.value { + dt_prop::PropValue::Integer(value) => Some(value), + _ => None, + }, + None => None, + } +} diff --git a/kernel/src/driver.rs b/kernel/src/driver.rs index 2ee74f16a..feef34e20 100644 --- a/kernel/src/driver.rs +++ b/kernel/src/driver.rs @@ -5,8 +5,8 @@ //! Driver support. use crate::{ - println, - synchronization::{interface::Mutex, NullLock}, + println, + synchronization::{interface::Mutex, NullLock}, }; //-------------------------------------------------------------------------------------------------- @@ -16,8 +16,8 @@ use crate::{ const NUM_DRIVERS: usize = 5; struct DriverManagerInner { - next_index: usize, - descriptors: [Option; NUM_DRIVERS], + next_index: usize, + descriptors: [Option; NUM_DRIVERS], } //-------------------------------------------------------------------------------------------------- @@ -26,20 +26,20 @@ struct DriverManagerInner { /// Driver interfaces. pub mod interface { - /// Device Driver functions. - pub trait DeviceDriver { - /// Return a compatibility string for identifying the driver. - fn compatible(&self) -> &'static str; - - /// Called by the kernel to bring up the device. - /// - /// # Safety - /// - /// - During init, drivers might do stuff with system-wide impact. - unsafe fn init(&self) -> Result<(), &'static str> { - Ok(()) - } - } + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &'static str; + + /// Called by the kernel to bring up the device. + /// + /// # Safety + /// + /// - During init, drivers might do stuff with system-wide impact. + unsafe fn init(&self) -> Result<(), &'static str> { + Ok(()) + } + } } /// Tpye to be used as an optional callback after a driver's init() has run. @@ -48,13 +48,13 @@ pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>; /// A descriptor for device drivers. #[derive(Copy, Clone)] pub struct DeviceDriverDescriptor { - device_driver: &'static (dyn interface::DeviceDriver + Sync), - post_init_callback: Option, + device_driver: &'static (dyn interface::DeviceDriver + Sync), + post_init_callback: Option, } /// Provides device driver management functions. pub struct DriverManager { - inner: NullLock, + inner: NullLock, } //-------------------------------------------------------------------------------------------------- @@ -68,13 +68,13 @@ static DRIVER_MANAGER: DriverManager = DriverManager::new(); //-------------------------------------------------------------------------------------------------- impl DriverManagerInner { - /// Create an instance. - pub const fn new() -> Self { - Self { - next_index: 0, - descriptors: [None; NUM_DRIVERS], - } - } + /// Create an instance. + pub const fn new() -> Self { + Self { + next_index: 0, + descriptors: [None; NUM_DRIVERS], + } + } } //-------------------------------------------------------------------------------------------------- @@ -82,86 +82,86 @@ impl DriverManagerInner { //-------------------------------------------------------------------------------------------------- impl DeviceDriverDescriptor { - /// Create an instance. - pub fn new( - device_driver: &'static (dyn interface::DeviceDriver + Sync), - post_init_callback: Option, - ) -> Self { - Self { - device_driver, - post_init_callback, - } - } + /// Create an instance. + pub fn new( + device_driver: &'static (dyn interface::DeviceDriver + Sync), + post_init_callback: Option, + ) -> Self { + Self { + device_driver, + post_init_callback, + } + } } /// Return a reference to the global DriverManager. pub fn driver_manager() -> &'static DriverManager { - &DRIVER_MANAGER + &DRIVER_MANAGER } impl DriverManager { - /// Create an instance. - pub const fn new() -> Self { - Self { - inner: NullLock::new(DriverManagerInner::new()), - } - } - - /// Register a device driver with the kernel. - pub fn register_driver(&self, descriptor: DeviceDriverDescriptor) { - self.inner.lock(|inner| { - inner.descriptors[inner.next_index] = Some(descriptor); - inner.next_index += 1; - }) - } - - /// Helper for iterating over registered drivers. - fn for_each_descriptor<'a>(&'a self, f: impl FnMut(&'a DeviceDriverDescriptor)) { - self.inner.lock(|inner| { - inner - .descriptors - .iter() - .filter_map(|x| x.as_ref()) - .for_each(f) - }) - } - - /// Fully initialize all drivers. - /// - /// # Safety - /// - /// - During init, drivers might do stuff with system-wide impact. - pub unsafe fn init_drivers(&self) { - self.for_each_descriptor(|descriptor| { - // 1. Initialize driver. - if let Err(x) = descriptor.device_driver.init() { - panic!( - "Error initializing driver: {}: {}", - descriptor.device_driver.compatible(), - x - ); - } - - // 2. Call corresponding post init callback. - if let Some(callback) = &descriptor.post_init_callback { - if let Err(x) = callback() { - panic!( - "Error during driver post-init callback: {}: {}", - descriptor.device_driver.compatible(), - x - ); - } - } - }); - } - - /// Enumerate all registered device drivers. - pub fn enumerate(&self) { - let mut i: usize = 1; - self.for_each_descriptor(|descriptor| { - println!(" {}. {}", i, descriptor.device_driver.compatible()); - - i += 1; - }); - } -} \ No newline at end of file + /// Create an instance. + pub const fn new() -> Self { + Self { + inner: NullLock::new(DriverManagerInner::new()), + } + } + + /// Register a device driver with the kernel. + pub fn register_driver(&self, descriptor: DeviceDriverDescriptor) { + self.inner.lock(|inner| { + inner.descriptors[inner.next_index] = Some(descriptor); + inner.next_index += 1; + }) + } + + /// Helper for iterating over registered drivers. + fn for_each_descriptor<'a>(&'a self, f: impl FnMut(&'a DeviceDriverDescriptor)) { + self.inner.lock(|inner| { + inner + .descriptors + .iter() + .filter_map(|x| x.as_ref()) + .for_each(f) + }) + } + + /// Fully initialize all drivers. + /// + /// # Safety + /// + /// - During init, drivers might do stuff with system-wide impact. + pub unsafe fn init_drivers(&self) { + self.for_each_descriptor(|descriptor| { + // 1. Initialize driver. + if let Err(x) = descriptor.device_driver.init() { + panic!( + "Error initializing driver: {}: {}", + descriptor.device_driver.compatible(), + x + ); + } + + // 2. Call corresponding post init callback. + if let Some(callback) = &descriptor.post_init_callback { + if let Err(x) = callback() { + panic!( + "Error during driver post-init callback: {}: {}", + descriptor.device_driver.compatible(), + x + ); + } + } + }); + } + + /// Enumerate all registered device drivers. + pub fn enumerate(&self) { + let mut i: usize = 1; + self.for_each_descriptor(|descriptor| { + println!(" {}. {}", i, descriptor.device_driver.compatible()); + + i += 1; + }); + } +} diff --git a/kernel/src/fs/init_ram_fs.rs b/kernel/src/fs/init_ram_fs.rs new file mode 100644 index 000000000..06783c076 --- /dev/null +++ b/kernel/src/fs/init_ram_fs.rs @@ -0,0 +1,132 @@ +/* +https://man.freebsd.org/cgi/man.cgi?query=cpio&sektion=5 +https://github.com/rcore-os/cpio/blob/main/src/lib.rs +*/ +extern crate alloc; + +use crate::println; + +const CPIO_END: &str = "TRAILER!!!\0"; +const MAGIC_NUMBER: &[u8] = b"070701"; +const HEADER_LEN: usize = 110; + +pub(crate) struct Cpio { + data: *const u8, +} + +#[repr(C, packed)] +struct CpioNewcHeader { + c_magic: [u8; 6], + c_ino: [u8; 8], + c_mode: [u8; 8], + c_uid: [u8; 8], + c_gid: [u8; 8], + c_nlink: [u8; 8], + c_mtime: [u8; 8], + c_filesize: [u8; 8], + c_devmajor: [u8; 8], + c_devminor: [u8; 8], + c_rdevmajor: [u8; 8], + c_rdevminor: [u8; 8], + c_namesize: [u8; 8], + c_check: [u8; 8], +} + +impl Cpio { + pub fn load(data: *const u8) -> Cpio { + Cpio { data } + } + + pub fn print_file_list(&self) { + let mut address = self.data; + loop { + let header = unsafe { &*(address as *const CpioNewcHeader) }; + if header.c_magic != *MAGIC_NUMBER { + println!("[InitRamFs] Invalid CPIO magic"); + break; + } + let namesize = u64::from_str_radix( + unsafe { core::str::from_utf8_unchecked(&header.c_namesize) }, + 16, + ) + .unwrap(); + let filesize = u64::from_str_radix( + unsafe { core::str::from_utf8_unchecked(&header.c_filesize) }, + 16, + ) + .unwrap(); + let name = unsafe { + core::str::from_utf8_unchecked(core::slice::from_raw_parts( + address.add(HEADER_LEN), + namesize as usize, + )) + }; + + if name == CPIO_END { + break; + } + + println!("{} {}", filesize, name); + + address = unsafe { address.add(pad_to_4(HEADER_LEN + namesize as usize)) }; + address = unsafe { address.add(pad_to_4(filesize as usize)) }; + } + } + + pub fn get_file(&self, filename: &str) -> Option<&[u8]> { + let mut address = self.data; + loop { + let header = unsafe { &*(address as *const CpioNewcHeader) }; + if header.c_magic != *MAGIC_NUMBER { + break; + } + let namesize = u64::from_str_radix( + unsafe { core::str::from_utf8_unchecked(&header.c_namesize) }, + 16, + ) + .unwrap(); + let filesize = u64::from_str_radix( + unsafe { core::str::from_utf8_unchecked(&header.c_filesize) }, + 16, + ) + .unwrap(); + let name = unsafe { + core::str::from_utf8_unchecked(core::slice::from_raw_parts( + address.add(HEADER_LEN), + namesize as usize, + )) + }; + + if name == CPIO_END { + break; + } + + address = unsafe { address.add(pad_to_4(HEADER_LEN + namesize as usize)) }; + + let mut found = true; + for i in 0..namesize as usize { + if name.as_bytes()[i] == 0 { + break; + } + if name.as_bytes()[i] != filename.as_bytes()[i] { + found = false; + break; + } + } + + if found { + return Some(unsafe { core::slice::from_raw_parts(address, filesize as usize) }); + } + + address = unsafe { address.add(pad_to_4(filesize as usize)) }; + } + None + } +} + +fn pad_to_4(len: usize) -> usize { + match len % 4 { + 0 => len, + x => len + (4 - x), + } +} diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs new file mode 100644 index 000000000..6939b67b6 --- /dev/null +++ b/kernel/src/fs/mod.rs @@ -0,0 +1 @@ +pub mod init_ram_fs; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 2e7f0d4d7..0c9ac1ac8 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1 +1 @@ -#![no_std] \ No newline at end of file +#![no_std] diff --git a/kernel/src/main.rs b/kernel/src/main.rs index fe882cba9..abfd11b89 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -5,20 +5,26 @@ #![no_main] #![no_std] -use core::ptr::write_volatile; - +mod arrsting; mod bsp; mod console; mod cpu; +mod device_tree; mod driver; +mod fs; +mod mbox; +mod memory; mod panic_wait; +mod power; mod print; +mod shell; mod synchronization; -mod arrsting; - -use arrsting::ArrString; +use crate::bsp::memory::map::sdram; +extern "C" { + static __dtb_address: u64; +} /// Early init code. /// /// # Safety @@ -35,12 +41,27 @@ unsafe fn kernel_init() -> ! { driver::driver_manager().init_drivers(); // println! is usable from here on. + // Initialize real memory + memory::ALLOCATOR.init(sdram::RAM_START, sdram::RAM_END); + + // let dtb_addr = 0x50000 as *const u8; + // let dtb_addr = unsafe { core::ptr::read_volatile(dtb_addr as *const u32) }; + + // loop{ + // println!("dtb_addr: {:#x}", dtb_addr); + // println!("dtb_addr_padding {:#x}", core::ptr::read_volatile(__dtb_address as *const u32)); + // } + + // Init device tree + let initrd_address = device_tree::get_initrd_start().unwrap(); + println!("initrd_address: {:#x}", initrd_address); + // Transition from unsafe to safe. kernel_main() } /// The main function running after the early init. -unsafe fn kernel_main() -> ! { +fn kernel_main() -> ! { use console::console; println!( @@ -56,86 +77,5 @@ unsafe fn kernel_main() -> ! { println!("[3] Chars written: {}", console().chars_written()); println!("[4] Echoing input now"); - let msg_buf_exceed = "[system] Buf size limit exceed, reset buf"; - - let msg_help = "help\t: print this help menu\r\nhello\t: print Hello World!\r\nreboot\t: reboot the device"; - let msg_hello_world = "HelloWorld!"; - let msg_not_found = "Command not found"; - let msg_reboot = "Rebooting..."; - - let arr_help = ArrString::new("help"); - let arr_hello: ArrString = ArrString::new("hello"); - let arr_reboot = ArrString::new("reboot"); - let arr_info = ArrString::new("info"); - - let mut buf = ArrString::new(""); - - // Discard any spurious received characters before going into echo mode. - console().clear_rx(); - - println!("TEST VER 0.0.2\r\n"); - print!("{}\r\n#", msg_help); - - loop { - let c = console().read_char(); - console().write_char(c); - - if c == '\n' { - if buf == arr_help { - println!("{}", msg_help); - } else if buf == arr_hello { - println!("{}", msg_hello_world); - } else if buf == arr_reboot { - println!("{}", msg_reboot); - reboot(); - } else if buf == arr_info { - println!("BoardVersion: {:x}", bsp::driver::MBOX.get_board_revision()); - println!( - "RAM: {} {}", - bsp::driver::MBOX.get_arm_memory().0, - bsp::driver::MBOX.get_arm_memory().1 - ); - } else { - println!("{}", msg_not_found); - } - - buf.clean_buf(); - print!("#"); - continue; - - // arrsting::arrstrcmp(buf, help); - } else if buf.get_len() == 1024 { - buf.clean_buf(); - println!("{}\r\n#", msg_buf_exceed); - print!("#"); - continue; - } else { - buf.push_char(c); - } - } -} - -unsafe fn reboot() { - reset(100); -} - -const PM_PASSWORD: u32 = 0x5a000000; -const PM_RSTC: u32 = 0x3F10_001C; -const PM_WDOG: u32 = 0x3F10_0024; - -pub fn reset(tick: u32) { - unsafe { - let mut r = PM_PASSWORD | 0x20; - write_volatile(PM_RSTC as *mut u32, r); - r = PM_PASSWORD | tick; - write_volatile(PM_WDOG as *mut u32, r); - } -} - -pub fn cancel_reset() { - unsafe { - let r = PM_PASSWORD | 0; - write_volatile(PM_RSTC as *mut u32, r); - write_volatile(PM_WDOG as *mut u32, r); - } + shell::start_shell(); } diff --git a/kernel/src/mbox/mod.rs b/kernel/src/mbox/mod.rs new file mode 100644 index 000000000..126455c54 --- /dev/null +++ b/kernel/src/mbox/mod.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2023 Andre Richter + +mod null_mbox; + +use crate::synchronization::{self, NullLock}; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +/// Console interfaces. +#[allow(dead_code)] +pub mod interface { + + pub trait Boardinfo { + fn get_board_revision(&self) -> u32 { + 0 + } + + fn get_arm_memory(&self) -> (u32, u32) { + (0, 0) + } + } + + /// Trait alias for a full-fledged console. + pub trait All: Boardinfo {} +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static CUR_MBOX: NullLock<&'static (dyn interface::All + Sync)> = + NullLock::new(&null_mbox::NULL_MBOX); + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +use synchronization::interface::Mutex; + +/// Register a new console. +pub fn register_mbox(new_mbox: &'static (dyn interface::All + Sync)) { + CUR_MBOX.lock(|con| *con = new_mbox); +} + +/// Return a reference to the currently registered console. +/// +/// This is the global console used by all printing macros. +pub fn mbox() -> &'static dyn interface::All { + CUR_MBOX.lock(|con| *con) +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ diff --git a/kernel/src/mbox/null_mbox.rs b/kernel/src/mbox/null_mbox.rs new file mode 100644 index 000000000..aaba00587 --- /dev/null +++ b/kernel/src/mbox/null_mbox.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 Andre Richter + +//! Null console. + +use super::interface; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +pub struct NullMBOX; + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +pub static NULL_MBOX: NullMBOX = NullMBOX {}; + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl interface::Boardinfo for NullMBOX { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn get_arm_memory(&self) -> (u32, u32) { + (0, 0) + } + + fn get_board_revision(&self) -> u32 { + 0 + } +} + +impl interface::All for NullMBOX {} diff --git a/kernel/src/memory/bump_allocator.rs b/kernel/src/memory/bump_allocator.rs new file mode 100644 index 000000000..9a8c53983 --- /dev/null +++ b/kernel/src/memory/bump_allocator.rs @@ -0,0 +1,106 @@ +/* +inspired from https://os.phil-opp.com +*/ +use crate::synchronization::{interface::Mutex, NullLock}; +// use crate::{print, println}; + +// use crate::println; +use core::{ + alloc::{GlobalAlloc, Layout}, + usize, +}; + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +pub struct BumpAllocator { + inner: NullLock, +} + +struct BumpAllocatorInner { + heap_start: usize, + heap_end: usize, + next: usize, + allocations: usize, +} + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +impl BumpAllocatorInner { + // Creates a new empty bump allocator. + pub const fn new() -> Self { + BumpAllocatorInner { + heap_start: 0, + heap_end: 0, + next: 0, + allocations: 0, + } + } + /// Initializes the bump allocator with the given heap bounds. + /// + /// This method is unsafe because the caller must ensure that the given + /// memory range is unused. Also, this method must be called only once. + pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { + self.heap_start = heap_start; + self.heap_end = heap_start + heap_size; + self.next = heap_start; + } + + fn alloc(&mut self, layout: Layout) -> *mut u8 { + // println!("[Alloc] START alloc"); + let alloc_start = align_up(self.next, layout.align()); + // println!("[Alloc] alloc_start: {:#x}", alloc_start); + self.next = alloc_start + layout.size(); + self.allocations += 1; + alloc_start as *mut u8 + } + + #[allow(dead_code)] + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { + todo!(); + } +} + +fn align_up(addr: usize, align: usize) -> usize { + match addr % align { + 0 => addr, + remainder => addr + align - remainder, + } +} +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl BumpAllocator { + #[allow(dead_code)] + pub const COMPATIBLE: &'static str = "MEMORY_ALLOCATOR"; + /// Create an instance. + /// + /// # Safety + pub const fn new() -> Self { + Self { + inner: NullLock::new(BumpAllocatorInner::new()), + } + } + + pub unsafe fn init(&self, heap_start: usize, heap_size: usize) { + self.inner.lock(|inner| inner.init(heap_start, heap_size)); + } +} + +//------------------------------------------------------------------------------ +// OS Interface Code +//------------------------------------------------------------------------------ + +unsafe impl GlobalAlloc for BumpAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.inner.lock(|inner| inner.alloc(layout)) + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { + // todo!() + } +} diff --git a/kernel/src/memory/mod.rs b/kernel/src/memory/mod.rs new file mode 100644 index 000000000..380c0d2d2 --- /dev/null +++ b/kernel/src/memory/mod.rs @@ -0,0 +1,4 @@ +pub mod bump_allocator; + +#[global_allocator] +pub static ALLOCATOR: bump_allocator::BumpAllocator = bump_allocator::BumpAllocator::new(); diff --git a/kernel/src/panic_wait.rs b/kernel/src/panic_wait.rs index c10ffc9a7..6e4a719ce 100644 --- a/kernel/src/panic_wait.rs +++ b/kernel/src/panic_wait.rs @@ -61,4 +61,4 @@ fn panic(info: &PanicInfo) -> ! { ); cpu::wait_forever() -} \ No newline at end of file +} diff --git a/kernel/src/power.rs b/kernel/src/power.rs new file mode 100644 index 000000000..21dcef680 --- /dev/null +++ b/kernel/src/power.rs @@ -0,0 +1,27 @@ +use core::ptr::write_volatile; + +pub fn reboot() { + reset(100); +} + +const PM_PASSWORD: u32 = 0x5a000000; +const PM_RSTC: u32 = 0x3F10_001C; +const PM_WDOG: u32 = 0x3F10_0024; + +pub fn reset(tick: u32) { + unsafe { + let mut r = PM_PASSWORD | 0x20; + write_volatile(PM_RSTC as *mut u32, r); + r = PM_PASSWORD | tick; + write_volatile(PM_WDOG as *mut u32, r); + } +} + +#[allow(dead_code)] +pub fn cancel_reset() { + unsafe { + let r = PM_PASSWORD | 0; + write_volatile(PM_RSTC as *mut u32, r); + write_volatile(PM_WDOG as *mut u32, r); + } +} diff --git a/kernel/src/print.rs b/kernel/src/print.rs index e90dd4f38..80779f0c6 100644 --- a/kernel/src/print.rs +++ b/kernel/src/print.rs @@ -3,7 +3,6 @@ // Copyright (c) 2018-2023 Andre Richter //! Printing. - use crate::console; use core::fmt; @@ -34,4 +33,4 @@ macro_rules! println { $crate::print::_print(format_args!($($arg)*)); ($crate::print!("\r\n")); }) -} \ No newline at end of file +} diff --git a/kernel/src/shell.rs b/kernel/src/shell.rs new file mode 100644 index 000000000..a5013c191 --- /dev/null +++ b/kernel/src/shell.rs @@ -0,0 +1,98 @@ +extern crate alloc; + +use alloc::{boxed::Box, string::String}; +use core::ptr; + +use crate::{console::console, device_tree, fs, mbox, power, print, println}; + +pub fn start_shell() -> ! { + println!("TEST VER 0.0.3\r\n"); + print_help(); + + let mut input_string = String::from(""); + + loop { + let c = console().read_char(); + console().write_char(c); + input_string.push(c); + // println!("[shell] input_string: {}", input_string); + + if c == '\r' || c == '\n' { + let (cmd, arg1) = input_string.split_once(char::is_whitespace).unwrap(); + let arg1 = arg1.trim_start(); + // println!("[shell] cmd: {}, arg1: {}", cmd, arg1); + print!("\r"); + + match cmd { + "help" => { + print_help(); + } + "hello" => { + println!("Hello World!"); + } + "reboot" => { + println!("Rebooting..."); + power::reboot(); + } + "info" => { + println!("BoardVersion: {:x}", mbox::mbox().get_board_revision()); + // println!("BoardVersion: {:x}", bsp::driver::MBOX.get_board_revision()); + println!( + "RAM: {} {}", + mbox::mbox().get_arm_memory().0, + mbox::mbox().get_arm_memory().1 + ) + } + "alloc" => { + println!("Allocate test start!"); + check_alloc(); + println!("Allocate test End!"); + } + "ls" => { + let fs: fs::init_ram_fs::Cpio = fs::init_ram_fs::Cpio::load( + device_tree::get_initrd_start().unwrap() as *const u8, + ); + fs.print_file_list(); + } + "cat" => { + // memory::get_initramfs_files(arg_1); + let fs: fs::init_ram_fs::Cpio = fs::init_ram_fs::Cpio::load( + device_tree::get_initrd_start().unwrap() as *const u8, + ); + if let Some(data) = fs.get_file(arg1) { + print!("{}", core::str::from_utf8(data).unwrap()); + } else { + println!("File not found: {}", arg1); + } + } + _ => { + println!("Unknown command: {:?}", cmd); + } + } + input_string.clear(); + print!("#"); + } + } +} + +// This is a test for checking memory allocation +fn check_alloc() { + let long_lived = Box::new(1); // new + for i in 0..32 { + let x = Box::new(i); + println!("{}", i); + println! {"{:p}" ,ptr::addr_of!(*x)}; + assert_eq!(*x, i); + } + assert_eq!(*long_lived, 1); // new +} + +fn print_help() { + println!("help\t: print this help menu"); + println!("hello\t: print Hello World!"); + println!("reboot\t: reboot the device"); + println!("info\t: show the device info"); + println!("alloc\t: test allocator"); + println!("ls\t: list file"); + println!("cat\t: print file"); +} diff --git a/kernel/src/synchronization.rs b/kernel/src/synchronization.rs index b87cd46a0..94c83de1c 100644 --- a/kernel/src/synchronization.rs +++ b/kernel/src/synchronization.rs @@ -74,4 +74,4 @@ impl interface::Mutex for NullLock { f(data) } -} \ No newline at end of file +} diff --git a/kernel/test.log b/kernel/test.log new file mode 100644 index 000000000..9853b9b6f Binary files /dev/null and b/kernel/test.log differ diff --git a/resources/bcm2710-rpi-3-b-plus.dtb b/resources/bcm2710-rpi-3-b-plus.dtb new file mode 100644 index 000000000..471a6ae50 Binary files /dev/null and b/resources/bcm2710-rpi-3-b-plus.dtb differ diff --git a/rootfs/1.txt b/rootfs/1.txt new file mode 100644 index 000000000..d96dc9570 --- /dev/null +++ b/rootfs/1.txt @@ -0,0 +1 @@ +abcdef \ No newline at end of file diff --git a/rootfs/2.txt b/rootfs/2.txt new file mode 100644 index 000000000..712eee5f1 --- /dev/null +++ b/rootfs/2.txt @@ -0,0 +1 @@ +ghijk \ No newline at end of file diff --git a/rootfs/3.txt b/rootfs/3.txt new file mode 100644 index 000000000..4535be624 --- /dev/null +++ b/rootfs/3.txt @@ -0,0 +1,2 @@ +lmnop +qrs \ No newline at end of file diff --git a/rootfs/Makefile b/rootfs/Makefile new file mode 100644 index 000000000..b88d274f9 --- /dev/null +++ b/rootfs/Makefile @@ -0,0 +1,3 @@ + +all: ./1.txt ./2.txt./ 3.txt + find ./*.txt | cpio -o -H newc >! ../resources/initramfs.cpio \ No newline at end of file