Happen to pick up a random project (i.e. write a minimal bootloader) to get some hands on experience. So far, I've managed to build the 'bootloader' part of my project and everything seems to works.... except, I'm having a hard time figuring out why (it works).
After spending a day on this, I believe I'm missing something obvious and could use some help here.
Its my understanding that the 'cortex-m' crate contains definitions and implementations for all of the 'arm-cortex-m' core-peripherals.
So, I'm using the system control block structure defined in cortex_m::peripheral::SCB to set vtor or vector table offset register. (please see attached image)
While it works, I cant seem to figure out why. SCB has no other properties other than a phantomdata marker and vtor is defined in scb.rs i.e. cortex_m::peripheral::scb::RegisterBlock.
My question is how am I able to access vtor directly via SCB i.e. how are SCB and RegisterBlock connected.
I know this sounds like a I'm missing something really obvious but I haven't been able to figure this out. Appreciate it if someone can clear this up for me.
Another question.-
My version of a minimal bootlader used core::mem::transmute which the compiler tells me is incredibly unsafe. So, is the a better(cleaner) way of performing a jump from the bootloader to the app?
In case its needed - My target is an nrf52840 but for now I've only tested it with qemu.
As I understand it, the magic happens in the implementation of Deref for SCB.
Essentially this means that if you have a &SCB reference, you can use it where you need a reference to an scb::RegisterBlock, and it will always give you a RegisterBlock at address 0xE000_ED04.
I'm not sure which specific rules are leading to the result you're seeing, but I assume the . automatically creates a reference, which is then automatically coerced. Even if I'm wrong about the details, I'm positive that the Deref implementation is what makes this work.
Thank you @mjw and @hannobraun for taking the time out to help me with this. Appreciate it.
Here's what I found: I re-factored the code and dropped it in 'rust-playground'.
Couple of points:
Accessing RegisterBlock fields with a reference & to SCB (i.e. &SCB.vtor) automatically calls SCB's deref method.
A simple SCB reference does not make an implicit deref call.
The same holds true for direct access i.e. we can access RegisterBlock fields without referencing by using SCB.vtor. However there is something odd with this method of access.
Problem: When I try to use (like say print) the result of SCB.vtor, it results in a
Segmentation fault (on rust playground)
Or an exit code: 0xc0000005, STATUS_ACCESS_VIOLATION (on VScode)
Any idea why this is the case?
use core::ops::Deref;
use core::marker::PhantomData;
fn address<T>(r: *const T) -> usize {
r as usize
}
#[derive(Debug)]
pub struct RegisterBlock {
pub icsr: u32, // Interrupt Control and State
pub vtor: u32, // Vector Table Offset (not present on Cortex-M0 variants)
}
#[derive(Debug)]
pub struct SCB {
_marker: PhantomData<*const ()>,
}
impl SCB {
/// Returns a pointer to the register block
#[inline(always)]
pub fn ptr() -> *const RegisterBlock {
0xE000_ED04 as *const _
}
}
impl Deref for SCB {
type Target = RegisterBlock;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe {
println!("deref called");
&*Self::ptr() }
}
}
fn main() {
let x = SCB { _marker: PhantomData };
assert_eq!(address(& x.icsr), 0xE000_ED04); // deref called auotmatically when accessing icsr via &SCB
assert_eq!(address(& x.vtor), 0xE000_ED08); // deref called automatically when accessing vtor via &SCB
println!(" {:X}", address(& x.icsr)); // deref called automatically when printing the value of icsr via &SCB
println!(" {:X}", address(& x.vtor)); // deref called automatically when printing the value of vtor via &SCB
println!(" {:?}", &x); // **deref NOT called** when you simply take a reference to SCB
let _y = x.icsr; // deref called automatically when accessing icsr via 'SCB'
// println!("{:#?}", y); //not sure why this isn't working. uncommenting this line gives me a 'seg fault'
let _z = x.vtor; // deref called automatically when accessing vtor via 'SCB'
// println!("{:#?}", z) //not sure why this isn't working. uncommenting this line gives me a 'seg fault'
}
Yep, I guess that clears it up. Completely forgot I was working on rust-playground
Just an observation: I think this deref thing is pretty confusing and convoluted. To the layman, this feels like you can declare one struct and have it magically reach into another (struct's) fields.