Skip to content

Commit

Permalink
PhysRange, make better use of devicetree
Browse files Browse the repository at this point in the history
Add PhysRange struct
Use DeviceTree for aarch64 drivers

Signed-off-by: Graham MacDonald <[email protected]>
  • Loading branch information
gmacd committed Jan 24, 2024
1 parent ce0896b commit dbcba36
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 85 deletions.
15 changes: 4 additions & 11 deletions aarch64/src/devcons.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// Racy to start.

use crate::registers::rpi_mmio;
use crate::param::KZERO;
use crate::uartmini::MiniUart;
use core::mem::MaybeUninit;
use port::devcons::Console;
use port::fdt::DeviceTree;
use port::mem::VirtRange;

// The aarch64 devcons implementation is focussed on Raspberry Pi 3, 4 for now.

Expand All @@ -32,16 +31,10 @@ use port::mem::VirtRange;
// https://wiki.osdev.org/Detecting_Raspberry_Pi_Board
// - Break out mailbox, gpio code

pub fn init(_dt: &DeviceTree) {
pub fn init(dt: &DeviceTree) {
Console::new(|| {
let mmio = rpi_mmio().expect("mmio base detect failed").to_virt();
let gpio_range = VirtRange::with_len(mmio + 0x20_0000, 0xb4);
let aux_range = VirtRange::with_len(mmio + 0x21_5000, 0x8);
let miniuart_range = VirtRange::with_len(mmio + 0x21_5040, 0x40);

let uart = MiniUart { gpio_range, aux_range, miniuart_range };
//let uart = MiniUart::new(dt);
// uart.init();
let uart = MiniUart::new(dt, KZERO);
uart.init();

static mut UART: MaybeUninit<MiniUart> = MaybeUninit::uninit();
unsafe {
Expand Down
73 changes: 51 additions & 22 deletions aarch64/src/kmem.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use port::fdt::RegBlock;

use crate::{param::KZERO, vm::Page4K};
use core::{
fmt,
Expand Down Expand Up @@ -76,16 +78,6 @@ impl PhysAddr {
pub const fn round_down(&self, step: u64) -> PhysAddr {
PhysAddr(self.0 & !(step - 1))
}

pub fn step_by_rounded(
startpa: PhysAddr,
endpa: PhysAddr,
step_size: usize,
) -> StepBy<Range<Self>> {
let startpa = startpa.round_down(step_size as u64);
let endpa = endpa.round_up(step_size as u64);
(startpa..endpa).step_by(step_size)
}
}

impl ops::Add<u64> for PhysAddr {
Expand Down Expand Up @@ -125,6 +117,46 @@ impl fmt::Debug for PhysAddr {
}
}

pub struct PhysRange(pub Range<PhysAddr>);

impl PhysRange {
pub fn with_len(start: u64, len: usize) -> Self {
Self(PhysAddr(start)..PhysAddr(start + len as u64))
}

#[allow(dead_code)]
pub fn offset_addr(&self, offset: u64) -> Option<PhysAddr> {
let addr = self.0.start + offset;
if self.0.contains(&addr) {
Some(addr)
} else {
None
}
}

pub fn start(&self) -> PhysAddr {
self.0.start
}

pub fn end(&self) -> PhysAddr {
self.0.end
}

pub fn step_by_rounded(&self, step_size: usize) -> StepBy<Range<PhysAddr>> {
let startpa = self.start().round_down(step_size as u64);
let endpa = self.end().round_up(step_size as u64);
(startpa..endpa).step_by(step_size)
}
}

impl From<&RegBlock> for PhysRange {
fn from(r: &RegBlock) -> Self {
let start = PhysAddr(r.addr);
let end = start + r.len.unwrap_or(0);
PhysRange(start..end)
}
}

unsafe fn page_slice_mut<'a>(pstart: *mut Page4K, pend: *mut Page4K) -> &'a mut [Page4K] {
let ustart = pstart.addr();
let uend = pend.addr();
Expand All @@ -150,28 +182,25 @@ mod tests {

#[test]
fn physaddr_step() {
let startpa = PhysAddr::new(4096);
let endpa = PhysAddr::new(4096 * 3);
let pas =
PhysAddr::step_by_rounded(startpa, endpa, vm::PAGE_SIZE_4K).collect::<Vec<PhysAddr>>();
let range = PhysRange(PhysAddr::new(4096)..PhysAddr::new(4096 * 3));
let pas = range.step_by_rounded(vm::PAGE_SIZE_4K).collect::<Vec<PhysAddr>>();
assert_eq!(pas, [PhysAddr::new(4096), PhysAddr::new(4096 * 2)]);
}

#[test]
fn physaddr_step_rounds_up_and_down() {
let startpa = PhysAddr::new(9000); // Should round down to 8192
let endpa = PhysAddr::new(5000 * 3); // Should round up to 16384
let pas =
PhysAddr::step_by_rounded(startpa, endpa, vm::PAGE_SIZE_4K).collect::<Vec<PhysAddr>>();
// Start should round down to 8192
// End should round up to 16384
let range = PhysRange(PhysAddr::new(9000)..PhysAddr::new(5000 * 3));
let pas = range.step_by_rounded(vm::PAGE_SIZE_4K).collect::<Vec<PhysAddr>>();
assert_eq!(pas, [PhysAddr::new(4096 * 2), PhysAddr::new(4096 * 3)]);
}

#[test]
fn physaddr_step_2m() {
let startpa = PhysAddr::new(0x3f000000);
let endpa = PhysAddr::new(0x3f000000 + 4 * 1024 * 1024);
let pas =
PhysAddr::step_by_rounded(startpa, endpa, vm::PAGE_SIZE_2M).collect::<Vec<PhysAddr>>();
let range =
PhysRange(PhysAddr::new(0x3f000000)..PhysAddr::new(0x3f000000 + 4 * 1024 * 1024));
let pas = range.step_by_rounded(vm::PAGE_SIZE_2M).collect::<Vec<PhysAddr>>();
assert_eq!(pas, [PhysAddr::new(0x3f000000), PhysAddr::new(0x3f000000 + 2 * 1024 * 1024)]);
}
}
15 changes: 5 additions & 10 deletions aarch64/src/mailbox.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::io::{read_reg, write_reg};
use crate::registers::rpi_mmio;
use crate::param::KZERO;
use core::mem;
use core::mem::MaybeUninit;
use port::fdt::DeviceTree;
Expand All @@ -18,17 +18,13 @@ static MAILBOX: Lock<Option<&'static mut Mailbox>> = Lock::new("mailbox", None);
/// Mailbox init. Mainly initialises a lock to ensure only one mailbox request
/// can be made at a time. We have no heap at this point, so creating a mailbox
/// that can be initialised based off the devicetree is rather convoluted.
pub fn init(_dt: &DeviceTree) {
pub fn init(dt: &DeviceTree) {
static mut NODE: LockNode = LockNode::new();
let mut mailbox = MAILBOX.lock(unsafe { &NODE });
*mailbox = Some({
static mut MAYBE_MAILBOX: MaybeUninit<Mailbox> = MaybeUninit::uninit();
unsafe {
let mmio = rpi_mmio().expect("mmio base detect failed").to_virt();
let mbox_range = VirtRange::with_len(mmio + 0xb880, 0x40);

MAYBE_MAILBOX.write(Mailbox { mbox_range });
//MAYBE_MAILBOX.write(Mailbox::new(dt, KZERO));
MAYBE_MAILBOX.write(Mailbox::new(dt, KZERO));
MAYBE_MAILBOX.assume_init_mut()
}
});
Expand All @@ -40,17 +36,16 @@ struct Mailbox {
pub mbox_range: VirtRange,
}

#[allow(dead_code)]
impl Mailbox {
fn new(dt: &DeviceTree, mmio_virt_offset: u64) -> Mailbox {
fn new(dt: &DeviceTree, mmio_virt_offset: usize) -> Mailbox {
Mailbox {
mbox_range: VirtRange::from(
&dt.find_compatible("brcm,bcm2835-mbox")
.next()
.and_then(|uart| dt.property_translated_reg_iter(uart).next())
.and_then(|reg| reg.regblock())
.unwrap()
.with_offset(mmio_virt_offset),
.with_offset(mmio_virt_offset as u64),
),
}
}
Expand Down
15 changes: 8 additions & 7 deletions aarch64/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod uartmini;
mod uartpl011;
mod vm;

use crate::kmem::PhysAddr;
use crate::kmem::{PhysAddr, PhysRange};
use crate::vm::kernel_root;
use core::ffi::c_void;
use port::fdt::DeviceTree;
Expand Down Expand Up @@ -99,29 +99,30 @@ fn print_board_info() {
println!(" Firmware Revision: {fw_revision:#010x}");
}

/// dtb_va is the virtual address of the DTB structure. The physical address is
/// assumed to be dtb_va-KZERO.
#[no_mangle]
pub extern "C" fn main9(dtb_ptr: usize) {
pub extern "C" fn main9(dtb_va: usize) {
trap::init();

// Parse the DTB before we set up memory so we can correctly map it
let dt = unsafe { DeviceTree::from_usize(dtb_ptr).unwrap() };
let dt = unsafe { DeviceTree::from_usize(dtb_va).unwrap() };

// Set up uart so we can log as early as possible
mailbox::init(&dt);
devcons::init(&dt);

println!();
println!("r9 from the Internet");
println!("DTB found at: {:#x}", dtb_ptr);
println!("DTB found at: {:#x}", dtb_va);
println!("midr_el1: {:?}", registers::MidrEl1::read());

// Map address space accurately using rust VM code to manage page tables
unsafe {
kalloc::free_pages(kmem::early_pages());

let dtb_phys = PhysAddr::from_virt(dtb_ptr as usize);
let edtb_phys = dtb_phys + dt.size() as u64;
vm::init(&mut KPGTBL, dtb_phys, edtb_phys);
let dtb_range = PhysRange::with_len(PhysAddr::from_virt(dtb_va).addr(), dt.size());
vm::init(&dt, &mut KPGTBL, dtb_range);
vm::switch(&KPGTBL);
}

Expand Down
15 changes: 8 additions & 7 deletions aarch64/src/registers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(non_upper_case_globals)]

use crate::kmem::PhysAddr;
use crate::{kmem::PhysRange, vm::PAGE_SIZE_2M};
use bitstruct::bitstruct;
use core::fmt;
use num_enum::TryFromPrimitive;
Expand Down Expand Up @@ -90,18 +90,19 @@ pub enum PartNum {
}

impl PartNum {
/// Return the physical MMIO base address for the Raspberry Pi MMIO
pub fn mmio(&self) -> Option<PhysAddr> {
/// Return the physical MMIO base range for the Raspberry Pi MMIO
pub fn mmio(&self) -> Option<PhysRange> {
let len = 2 * PAGE_SIZE_2M;
match self {
Self::RaspberryPi1 => Some(PhysAddr::new(0x20000000)),
Self::RaspberryPi2 | Self::RaspberryPi3 => Some(PhysAddr::new(0x3f000000)),
Self::RaspberryPi4 => Some(PhysAddr::new(0xfe000000)),
Self::RaspberryPi1 => Some(PhysRange::with_len(0x20000000, len)),
Self::RaspberryPi2 | Self::RaspberryPi3 => Some(PhysRange::with_len(0x3f000000, len)),
Self::RaspberryPi4 => Some(PhysRange::with_len(0xfe000000, len)),
Self::Unknown => None,
}
}
}

pub fn rpi_mmio() -> Option<PhysAddr> {
pub fn rpi_mmio() -> Option<PhysRange> {
MidrEl1::read().partnum_enum().ok().and_then(|p| p.mmio())
}

Expand Down
2 changes: 1 addition & 1 deletion aarch64/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use port::mem::VirtRange;
// - Add support for raspi4
#[panic_handler]
pub fn panic(info: &PanicInfo) -> ! {
let mmio = rpi_mmio().expect("mmio base detect failed").to_virt();
let mmio = rpi_mmio().expect("mmio base detect failed").start().to_virt();

let gpio_range = VirtRange::with_len(mmio + 0x200000, 0xb4);
let aux_range = VirtRange::with_len(mmio + 0x215000, 0x8);
Expand Down
8 changes: 4 additions & 4 deletions aarch64/src/uartmini.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ pub struct MiniUart {

#[allow(dead_code)]
impl MiniUart {
pub fn new(dt: &DeviceTree, mmio_virt_offset: u64) -> MiniUart {
pub fn new(dt: &DeviceTree, mmio_virt_offset: usize) -> MiniUart {
// TODO use aliases?
let gpio_range = VirtRange::from(
&dt.find_compatible("brcm,bcm2835-gpio")
.next()
.and_then(|uart| dt.property_translated_reg_iter(uart).next())
.and_then(|reg| reg.regblock())
.unwrap()
.with_offset(mmio_virt_offset),
.with_offset(mmio_virt_offset as u64),
);

// Find a compatible aux
Expand All @@ -38,7 +38,7 @@ impl MiniUart {
.and_then(|uart| dt.property_translated_reg_iter(uart).next())
.and_then(|reg| reg.regblock())
.unwrap()
.with_offset(mmio_virt_offset),
.with_offset(mmio_virt_offset as u64),
);

// Find a compatible miniuart
Expand All @@ -48,7 +48,7 @@ impl MiniUart {
.and_then(|uart| dt.property_translated_reg_iter(uart).next())
.and_then(|reg| reg.regblock())
.unwrap()
.with_offset(mmio_virt_offset),
.with_offset(mmio_virt_offset as u64),
);

MiniUart { gpio_range, aux_range, miniuart_range }
Expand Down
Loading

0 comments on commit dbcba36

Please sign in to comment.