Reading from physical address 0x0

Hello,

I currently am working on an embedded device where the base address of the RAM is 0x0.
This is definitely a problem right now, because creating a slice where the base address is 0x0 seems to be really problematic.

I have this block where I am trying to write the currently running application to a non-volatile memory. For this, I tried to read the whole bootloader app as a slice, but the base address of 0x0 makes this really problematic. You can find the code here where I solves this issue using assembler:

    if FLASH_SELF {
        let bootloader_data = {
            unsafe {
                &*core::ptr::slice_from_raw_parts(
                    (BOOTLOADER_START_ADDR + 4) as *const u8,
                    (BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 8) as usize,
                )
            }
        };
        let mut first_four_bytes: [u8; 4] = [0; 4];
        unsafe {
            core::arch::asm!(
                "ldr r0, [{0}]",    // Load 4 bytes from src into r0 register
                "str r0, [{1}]",    // Store r0 register into first_four_bytes
                in(reg) BOOTLOADER_START_ADDR as *const u8,         // Input: src pointer (0x0)
                in(reg) &mut first_four_bytes as *mut [u8; 4],  // Input: destination pointer
            );
        }
        let mut digest = CRC_ALGO.digest();
        digest.update(&first_four_bytes);
        digest.update(bootloader_data);
        let bootloader_crc = digest.finalize();

        nvm.write_data(0x0, &first_four_bytes);
        nvm.write_data(0x4, bootloader_data);
        if let Err(e) = nvm.verify_data(0x0, &first_four_bytes) {
            rprintln!("verification of self-flash to NVM failed: {:?}", e);
        }
        if let Err(e) = nvm.verify_data(0x4, bootloader_data) {
            rprintln!("verification of self-flash to NVM failed: {:?}", e);
        }

        nvm.write_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes());
        if let Err(e) = nvm.verify_data(BOOTLOADER_CRC_ADDR, &bootloader_crc.to_be_bytes()) {
            rprintln!(
                "error: CRC verification for bootloader self-flash failed: {:?}",
                e
            );
        }
    }

I circumvented the issue by falling back to assembler, but this feels really hacky to me.. Shouldn't Rust be low level enough to allow me to deal with these issues? I found this pre-RFC: Pre-RFC: Conditionally-supported volatile access to address 0 - libs - Rust Internals .

Does anyone have other ideas how to deal with this issue or is the hack I used the only way to deal with this?

Thanks a lot in advance,
Robin

1 Like

Unfortunately for such targets, the only legal way to read address zero is with assembly. This is the case in C as well when the null address is address zero, even if C compilers for such targets often tend to try to avoid “miscompiling” code reading address zero.

5 Likes

Since ram starts at address zero (as opposed to having an mmio register at zero) couldn't you just reserve and nerver use the very first address?

Depends on how limited on ram you are, but one byte (or word, depending on alignment) isn't much. I believe WASM targets for Rust already so something similar.

not if the interrupt table or the like is there, like on some platforms

1 Like

And for those C platforms where the null address is something other than address zero, (void *)(uintptr_t)0 is an implementation-defined pointer that may resolve to something different than (void *)0. But as always you need to consult the documentation for you C compiler.

I'm not sure if Rust has an equivalent to this, other than inline asm.

2 Likes