rustberry 0.1.0

All-purpose Raspberry Pi library for Rust
Documentation
// Copyright (C) 2018  Adam Gausmann
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Memory access utilities.

use std::fs::OpenOptions;
use std::io;
use memmap::{MmapMut, MmapOptions};

/// A view into a memory map that splits it into "registers."
///
/// All reads and writes are 32-bit, as they are currently the only size 
/// required by BCM283x peripherals. This data structure is a convenience
/// for 32-bit memory operations at any offset within the mapped data.
///
/// Internally, the specified location in memory is mapped using the
/// `/dev/mem` file. Unfortunately, this method requires root privileges,
/// but this may not be such a problem in the majority of use cases for this
/// library.
///
/// Registers may only be accessed via getters and setters. There is no
/// option to request a (mutable) reference because it would be unsafe
/// to do so. Registers may change at any time regardless of the mutability
/// of the reference that is held (since they are controlled by the system),
/// so it is only valid to receive its current state and set it via another
/// separate call once the new data is ready.
/// 
/// # Safety
///
/// In general, the ability to access arbitrary memory is a _very_ dangerous
/// privilege to have. For convenience, the only unsafe operation is the
/// constructur which is when the memory is mapped. Once that has happened,
/// it is assumed that the user has accepted the risks.
#[derive(Debug)]
pub struct RegisterMap {
    map: MmapMut,
    len: usize,
}

impl RegisterMap {
    /// Constructs a map of the given location in physical memory. 
    ///
    /// `offset` and `len` are both specified in bytes.
    ///
    /// # Safety
    ///
    /// This method is unsafe due to the inherent danger of arbitrary memory
    /// access. The callee must ensure that the requested memory locations are
    /// safe for the program to access.
    pub unsafe fn map(offset: usize, len: usize)
        -> Result<RegisterMap, io::Error>
    {
        let mem = OpenOptions::new()
            .read(true).write(true)
            .open("/dev/mem")?;
        let map = MmapOptions::new()
            .offset(offset)
            .len(len)
            .map_mut(&mem)?;

        Ok(RegisterMap {
            map,
            len
        })
    }

    fn get(&self, loc: usize) -> &u32 {
        assert!(loc <= self.len - 4);
        unsafe {
            (self.map.as_ptr().offset(loc as isize) as *const u32)
                .as_ref().unwrap()
        }
    }
    
    fn get_mut(&mut self, loc: usize) -> &mut u32 {
        assert!(loc <= self.len - 4);
        unsafe {
            (self.map.as_mut_ptr().offset(loc as isize) as *mut u32)
                .as_mut().unwrap()
        }
    }

    /// Returns the current value of the register at `loc` bytes from the
    /// offset.
    ///
    /// # Panics
    ///
    /// Panics if the given location is out of bounds.
    pub fn load(&self, loc: usize) -> u32 {
        *self.get(loc)
    }

    /// Sets the value of the register `loc` bytes from the offset to the given
    /// `value`.
    ///
    /// # Panics
    ///
    /// Panics if the given location is out of bounds.
    pub fn store(&mut self, loc: usize, value: u32) {
        *self.get_mut(loc) = value;
    }
}