Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I declare a static variable as a reference to a hard-coded memory address?

Tags:

embedded

rust

ffi

I am working on embedded Rust code for LPC82X series controllers from NXP - the exact toolchain does not matter for the question.

These controllers contain peripheral drivers in ROM. I want to use these drivers, which means I need to use unsafe Rust and FFI without linking actual code.

The ROM APIs expose function pointers packed into C structs at specific address locations. If somebody wants the details of this API, chapter 29 of the LPC82X manual describes the API in question.

My Rust playground dummy sketch looks like this, that would be hidden from application code, by a yet unwritten I2C abstraction lib. This compiles.

#![feature(naked_functions)]

const I2C_ROM_API_ADDRESS: usize = 0x1fff_200c;
static mut ROM_I2C_API: Option<&RomI2cApi> = None;

#[repr(C)]
struct RomI2cApi {
    // Dummy functions, real ones take arguments, and have different return
    // These won't be called directly, only through the struct's implemented methods
    // value
    master_transmit_poll: extern "C" fn() -> bool,
    master_receive_poll: extern "C" fn() -> bool,
}

impl RomI2cApi {
    fn api_table() -> &'static RomI2cApi {
        unsafe {
            match ROM_I2C_API {
                None => RomI2cApi::new(),
                Some(table) => table,
            }
        }
    }

    unsafe fn new() -> &'static RomI2cApi {
        ROM_I2C_API = Some(&*(I2C_ROM_API_ADDRESS as *const RomI2cApi));
        ROM_I2C_API.unwrap()
    }

    #[inline]
    fn master_transmit_poll(&self) -> bool {
        (self.master_transmit_poll)()
    }

    #[inline]
    fn master_receive_poll(&self) -> bool {
        (self.master_receive_poll)()
    }
}

impl From<usize> for &'static RomI2cApi {
    fn from(address: usize) -> &'static RomI2cApi {
        unsafe { &*(address as *const RomI2cApi) }
    }
}

fn main() {
    let rom_api = unsafe { RomI2cApi::api_table() };
    println!("ROM I2C API address is: {:p}", rom_api);
    // Should be commented out when trying !
    rom_api.master_transmit_poll();
}

I cannot declare the function pointer structs as non-mutable static as statics have many restrictions, including not dereferencing pointers in the assignment. Is there a better workaround than Option? Using Option with the api_table function at least guarantees that initialization happens.

like image 796
Krisztián Szegi Avatar asked Feb 09 '18 08:02

Krisztián Szegi


1 Answers

You can get around having a static at all:

const ROM_I2C_API: &RomI2cApi = &*(0x1fff_200c as *const RomI2cApi);

Not yet working, but is planned to work in the future. For now use

const ROM_I2C_API: *const RomI2cApi = 0x1fff_200c as *const RomI2cApi;

fn api_table() -> &'static RomI2cApi {
    unsafe { &*(ROM_I2C_API) }
}

This creates a &'static RomI2cApi and allows you to access the functions everywhere directly by calling api_table().master_transmit_poll()

like image 59
oli_obk Avatar answered Oct 23 '22 03:10

oli_obk