Allocate Mailboxes from special non-cacheable memory
This commit is contained in:
parent
006abc39dd
commit
7ea82d90d7
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Andre Richter <andre.o.richter@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::println;
|
||||||
|
use core::alloc::{Alloc, AllocErr, Layout};
|
||||||
|
use core::mem;
|
||||||
|
use core::ptr::NonNull;
|
||||||
|
use core::slice;
|
||||||
|
|
||||||
|
pub struct BumpAllocator {
|
||||||
|
next: usize,
|
||||||
|
pool_end: usize,
|
||||||
|
name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Alloc for BumpAllocator {
|
||||||
|
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
|
||||||
|
let start = crate::memory::aligned_addr_unchecked(self.next, layout.align());
|
||||||
|
let end = start + layout.size();
|
||||||
|
|
||||||
|
if end <= self.pool_end {
|
||||||
|
self.next = end;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"[i] {}:\n Allocated Addr {:#010X} Size {:#X}",
|
||||||
|
self.name,
|
||||||
|
start,
|
||||||
|
layout.size()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(NonNull::new_unchecked(start as *mut u8))
|
||||||
|
} else {
|
||||||
|
Err(AllocErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A bump allocator doesn't care
|
||||||
|
unsafe fn dealloc(&mut self, _ptr: NonNull<u8>, _layout: Layout) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BumpAllocator {
|
||||||
|
pub const fn new(pool_start: usize, pool_end: usize, name: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
next: pool_start,
|
||||||
|
pool_end,
|
||||||
|
name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a zeroed slice
|
||||||
|
pub fn alloc_slice_zeroed<'a, T>(
|
||||||
|
&mut self,
|
||||||
|
count_of_items: usize,
|
||||||
|
alignment: usize,
|
||||||
|
) -> Result<&'a mut [T], ()> {
|
||||||
|
let l;
|
||||||
|
let size_in_byte = count_of_items * mem::size_of::<T>();
|
||||||
|
match Layout::from_size_align(size_in_byte, alignment) {
|
||||||
|
Ok(layout) => l = layout,
|
||||||
|
|
||||||
|
Err(_) => {
|
||||||
|
println!("[e] Layout Error!");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr;
|
||||||
|
match unsafe { self.alloc_zeroed(l) } {
|
||||||
|
Ok(i) => ptr = i.as_ptr(),
|
||||||
|
|
||||||
|
Err(_) => {
|
||||||
|
println!("[e] Layout Error!");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(unsafe { slice::from_raw_parts_mut(ptr as *mut T, count_of_items) })
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
// pub use self::area_frame_allocator::AreaFrameAllocator;
|
// pub use self::area_frame_allocator::AreaFrameAllocator;
|
||||||
|
|
||||||
|
mod bump_allocator;
|
||||||
|
pub use bump_allocator::BumpAllocator;
|
||||||
|
|
||||||
pub type PhysicalAddress = usize;
|
pub type PhysicalAddress = usize;
|
||||||
pub type VirtualAddress = usize;
|
pub type VirtualAddress = usize;
|
||||||
|
|
||||||
|
|
|
@ -260,11 +260,7 @@ pub unsafe fn init() {
|
||||||
barrier::isb(barrier::SY);
|
barrier::isb(barrier::SY);
|
||||||
|
|
||||||
// Enable the MMU and turn on data and instruction caching.
|
// Enable the MMU and turn on data and instruction caching.
|
||||||
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::NonCacheable + SCTLR_EL1::I::Cacheable);
|
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable);
|
||||||
|
|
||||||
// @todo potentially disable both caches here for testing?
|
|
||||||
// Figured: data caching causes these mailbox misreads
|
|
||||||
// Need to allocate mailbox in non-cached memory perhaps for proper hw i/o
|
|
||||||
|
|
||||||
// Force MMU init to complete before next instruction
|
// Force MMU init to complete before next instruction
|
||||||
/*
|
/*
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -9,6 +9,7 @@
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
#![feature(range_contains)]
|
#![feature(range_contains)]
|
||||||
#![feature(underscore_const_names)]
|
#![feature(underscore_const_names)]
|
||||||
|
#![feature(allocator_api)]
|
||||||
#![doc(html_root_url = "https://docs.metta.systems/")]
|
#![doc(html_root_url = "https://docs.metta.systems/")]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(unused_assignments)]
|
#![allow(unused_assignments)]
|
||||||
|
@ -54,6 +55,24 @@ use platform::{
|
||||||
/// The global console. Output of the print! and println! macros.
|
/// The global console. Output of the print! and println! macros.
|
||||||
static CONSOLE: sync::NullLock<devices::Console> = sync::NullLock::new(devices::Console::new());
|
static CONSOLE: sync::NullLock<devices::Console> = sync::NullLock::new(devices::Console::new());
|
||||||
|
|
||||||
|
/// The global allocator for DMA-able memory. That is, memory which is tagged
|
||||||
|
/// non-cacheable in the page tables.
|
||||||
|
static DMA_ALLOCATOR: sync::NullLock<memory::BumpAllocator> =
|
||||||
|
sync::NullLock::new(memory::BumpAllocator::new(
|
||||||
|
memory::map::virt::DMA_HEAP_START as usize,
|
||||||
|
memory::map::virt::DMA_HEAP_END as usize,
|
||||||
|
"Global DMA Allocator",
|
||||||
|
// Try the following arguments instead to see all mailbox operations
|
||||||
|
// fail. It will cause the allocator to use memory that are marked
|
||||||
|
// cacheable and therefore not DMA-safe. The answer from the VideoCore
|
||||||
|
// won't be received by the CPU because it reads an old cached value
|
||||||
|
// that resembles an error case instead.
|
||||||
|
|
||||||
|
// 0x00600000 as usize,
|
||||||
|
// 0x007FFFFF as usize,
|
||||||
|
// "Global Non-DMA Allocator",
|
||||||
|
));
|
||||||
|
|
||||||
fn init_jlink_rtt() {
|
fn init_jlink_rtt() {
|
||||||
CONSOLE.lock(|c| {
|
CONSOLE.lock(|c| {
|
||||||
c.replace_with(Output::new().into());
|
c.replace_with(Output::new().into());
|
||||||
|
@ -77,7 +96,7 @@ fn init_uart_serial() {
|
||||||
|
|
||||||
let uart = platform::PL011Uart::new_default();
|
let uart = platform::PL011Uart::new_default();
|
||||||
|
|
||||||
let mut mbox = platform::mailbox::Mailbox::new();
|
let mut mbox = platform::mailbox::Mailbox::default();
|
||||||
|
|
||||||
// uart.init() will reconfigure the GPIO, which causes a race against
|
// uart.init() will reconfigure the GPIO, which causes a race against
|
||||||
// the MiniUart that is still putting out characters on the physical
|
// the MiniUart that is still putting out characters on the physical
|
||||||
|
|
|
@ -11,12 +11,14 @@ use register::mmio::*;
|
||||||
// The address for the buffer needs to be 16-byte aligned
|
// The address for the buffer needs to be 16-byte aligned
|
||||||
// so that the VideoCore can handle it properly.
|
// so that the VideoCore can handle it properly.
|
||||||
// The reason is that lowest 4 bits of the address will contain the channel number.
|
// The reason is that lowest 4 bits of the address will contain the channel number.
|
||||||
#[repr(C)]
|
pub struct Mailbox<'a> {
|
||||||
#[repr(align(16))]
|
pub buffer: &'a mut [u32],
|
||||||
pub struct Mailbox {
|
base_addr: u32,
|
||||||
pub buffer: [u32; 36],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAILBOX_ALIGNMENT: usize = 16;
|
||||||
|
const MAILBOX_ITEMS_COUNT: usize = 36;
|
||||||
|
|
||||||
// Identity mapped first 1Gb by u-boot
|
// Identity mapped first 1Gb by u-boot
|
||||||
const MAILBOX_BASE: u32 = BcmHost::get_peripheral_address() + 0xb880;
|
const MAILBOX_BASE: u32 = BcmHost::get_peripheral_address() + 0xb880;
|
||||||
// Lowest 4-bits are channel ID.
|
// Lowest 4-bits are channel ID.
|
||||||
|
@ -292,15 +294,15 @@ fn read(regs: &RegisterBlock, expected: u32, channel: u32) -> Result<()> {
|
||||||
/// ```
|
/// ```
|
||||||
/// unsafe { (*Mbox::ptr()).STATUS.read() }
|
/// unsafe { (*Mbox::ptr()).STATUS.read() }
|
||||||
/// ```
|
/// ```
|
||||||
impl Deref for Mailbox {
|
impl<'a> Deref for Mailbox<'a> {
|
||||||
type Target = RegisterBlock;
|
type Target = RegisterBlock;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
unsafe { &*Self::ptr() }
|
unsafe { &*self.ptr() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Display for Mailbox {
|
impl<'a> core::fmt::Display for Mailbox<'a> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
let count = self.buffer[0] / 4;
|
let count = self.buffer[0] / 4;
|
||||||
assert_eq!(self.buffer[0], count * 4);
|
assert_eq!(self.buffer[0], count * 4);
|
||||||
|
@ -312,20 +314,47 @@ impl core::fmt::Display for Mailbox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Mailbox {
|
impl<'a> Default for Mailbox<'a> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new_default().expect("Couldn't allocate a mailbox")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mailbox {
|
impl<'a> Mailbox<'a> {
|
||||||
pub fn new() -> Mailbox {
|
pub fn new_default() -> ::core::result::Result<Mailbox<'a>, ()> {
|
||||||
Mailbox { buffer: [0; 36] }
|
let ret = crate::DMA_ALLOCATOR
|
||||||
|
.lock(|d| d.alloc_slice_zeroed(MAILBOX_ITEMS_COUNT, MAILBOX_ALIGNMENT));
|
||||||
|
|
||||||
|
if ret.is_err() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Mailbox {
|
||||||
|
base_addr: MAILBOX_BASE,
|
||||||
|
buffer: ret.unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(base_addr: usize) -> ::core::result::Result<Mailbox<'a>, ()> {
|
||||||
|
let ret = crate::DMA_ALLOCATOR
|
||||||
|
.lock(|d| d.alloc_slice_zeroed(MAILBOX_ITEMS_COUNT, MAILBOX_ALIGNMENT));
|
||||||
|
|
||||||
|
if ret.is_err() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
let base_addr = u32::try_from(base_addr).unwrap();
|
||||||
|
|
||||||
|
Ok(Mailbox {
|
||||||
|
base_addr,
|
||||||
|
buffer: ret.unwrap(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a pointer to the register block
|
/// Returns a pointer to the register block
|
||||||
fn ptr() -> *const RegisterBlock {
|
fn ptr(&self) -> *const RegisterBlock {
|
||||||
MAILBOX_BASE as *const _
|
self.base_addr as *const _
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&self, channel: u32) -> Result<()> {
|
pub fn write(&self, channel: u32) -> Result<()> {
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl VC {
|
||||||
// https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=185116
|
// https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=185116
|
||||||
pub fn init_fb(size: Size2d, depth: u32) -> Option<Display> {
|
pub fn init_fb(size: Size2d, depth: u32) -> Option<Display> {
|
||||||
// Use property channel
|
// Use property channel
|
||||||
let mut mbox = Mailbox::new();
|
let mut mbox = Mailbox::default();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* * All tags in the request are processed in one operation.
|
* * All tags in the request are processed in one operation.
|
||||||
|
|
Loading…
Reference in New Issue