summary refs log blame commit diff
path: root/devices/src/register_space/register.rs
blob: 2a4f551ded23d4f40840951f52450a203f3bf672 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                                         
                    
                                                    
                      

                                 

                         
                    
 

                                         
 
                                                                     

                                            


                             

 

                                                      



                                  

                                                                      



                                          
                    
                                            
                                                               


                                                       
                                                      

                                                                  
                                                                                 


                                      
                            























                                                                    
                             


                                          
                                










                                                                                                  

                             

                    
                                    




















                                                                                                         





                                                           






                                                 
                               


























                                                                                                

                                      




                                                               

                                                           
                                                                






                                                               
                                     







                                                                              

                                                                                                  

                            
                               










                                                                                  
                                                 



















                                                                               
                         




                                                          
                                      

                                 
                       




                                                          

                                                           
                                      


                                                      


                                                        






                                                                  
                                                                       






                                                                         
                                                 






                                                                     
         







                                                                                                  






















                                                                                                
                                               





                                           
                         







                                                                                                

                                                                                       







                                                                                 
                                                              





























                                                                             
                                     









                                                                                  
                                     























                                                           
                                     

                                                   
                                                                        
                                                               





























                                                                           


                                      
                                        
                             






                                                           


                                      
                                        
                             




                                         
                                                                       




                                             


                                      
                                        
                             













                                             


                                      
                                          
                             

                                             
                          

















                                             


                                      

                                       
                          

















                                                    


                                      

                                          
                          

















                                                        


                                      

                                             
                          




















                                              
                                   


                                            
                          
                                       
                          
                                       
                                   
                          
                                       

     
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::boxed::Box;
use std::cmp::{max, min, Ord, Ordering, PartialOrd};
use std::mem::size_of;
use std::sync::{Arc, MutexGuard};
use sync::Mutex;

use data_model::DataInit;
use sys_util::error;

/// Type of offset in the register space.
pub type RegisterOffset = u64;

/// This represents a range of memory in the register space starting.
/// Both from and to are inclusive.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct RegisterRange {
    pub from: RegisterOffset,
    pub to: RegisterOffset,
}

impl Ord for RegisterRange {
    fn cmp(&self, other: &RegisterRange) -> Ordering {
        self.from.cmp(&other.from)
    }
}

impl PartialOrd for RegisterRange {
    fn partial_cmp(&self, other: &RegisterRange) -> Option<Ordering> {
        self.from.partial_cmp(&other.from)
    }
}

impl RegisterRange {
    /// Return true if those range overlaps.
    pub fn overlap_with(&self, other: &RegisterRange) -> bool {
        !(self.from > other.to || self.to < other.from)
    }

    /// Get the overlapping part of two RegisterRange.
    /// Return is Option(overlap_from, overlap_to).
    /// For example, (4,7).overlap_range(5, 8) will be Some(5, 7).
    pub fn overlap_range(&self, other: &RegisterRange) -> Option<RegisterRange> {
        if !self.overlap_with(other) {
            return None;
        }
        Some(RegisterRange {
            from: max(self.from, other.from),
            to: min(self.to, other.to),
        })
    }
}

/// RegisterValue trait should be satisfied by register value types.
pub trait RegisterValue:
    'static
    + Into<u64>
    + Clone
    + DataInit
    + std::ops::BitOr<Self, Output = Self>
    + std::ops::BitAnd<Self, Output = Self>
    + std::ops::Not<Output = Self>
    + std::fmt::LowerHex
{
    // Get byte of the offset.
    fn get_byte(&self, offset: usize) -> u8 {
        let val: u64 = self.clone().into();
        (val >> (offset * 8)) as u8
    }
    // Set masked bits.
    fn set_bits(&mut self, mask: Self) {
        *self = *self | mask;
    }
    // Clear masked bits.
    fn clear_bits(&mut self, mask: Self) {
        *self = *self & (!mask);
    }
}
impl RegisterValue for u8 {}
impl RegisterValue for u16 {}
impl RegisterValue for u32 {}
impl RegisterValue for u64 {}

// Helper function to read a register. If the read range overlaps with value's range, it will load
// corresponding bytes into data.
fn read_reg_helper<T: RegisterValue>(
    val: T,
    val_range: RegisterRange,
    addr: RegisterOffset,
    data: &mut [u8],
) {
    let read_range = RegisterRange {
        from: addr,
        to: addr + data.len() as u64 - 1,
    };

    let overlap = match val_range.overlap_range(&read_range) {
        Some(overlap) => overlap,
        None => {
            error!("calling read_reg_helper with non overlapping range. mmio_register might have a bug");
            return;
        }
    };
    let val_start_idx = (overlap.from - val_range.from) as usize;
    let read_start_idx = (overlap.from - read_range.from) as usize;
    let total_size = (overlap.to - overlap.from) as usize + 1;
    for i in 0..total_size {
        data[read_start_idx + i] = val.get_byte(val_start_idx + i);
    }
}

/// Interface for register, as seen by guest driver.
pub trait RegisterInterface: Send {
    /// Range of this register.
    fn range(&self) -> RegisterRange;
    /// Handle read.
    fn read(&self, addr: RegisterOffset, data: &mut [u8]);
    /// Handle write.
    fn write(&self, _addr: RegisterOffset, _data: &[u8]) {}
    /// Reset this register to default value.
    fn reset(&self) {}
}

// Spec for hardware init Read Only Registers.
// The value of this register won't change.
pub struct StaticRegisterSpec<T: RegisterValue> {
    pub offset: RegisterOffset,
    pub value: T,
}

/// A static register is a register inited by hardware. The value won't change in it's lifetime.
/// All functions implemented on this one is thread safe.
#[derive(Clone)]
pub struct StaticRegister<T>
where
    T: RegisterValue,
{
    spec: &'static StaticRegisterSpec<T>,
}

impl<T> StaticRegister<T>
where
    T: RegisterValue,
{
    /// Create an new static register from spec.
    pub fn new(spec: &'static StaticRegisterSpec<T>) -> StaticRegister<T> {
        StaticRegister { spec }
    }
}

impl<T> RegisterInterface for StaticRegister<T>
where
    T: RegisterValue,
{
    fn range(&self) -> RegisterRange {
        RegisterRange {
            from: self.spec.offset,
            to: self.spec.offset + (size_of::<T>() as u64) - 1,
        }
    }

    fn read(&self, addr: RegisterOffset, data: &mut [u8]) {
        let val_range = self.range();
        read_reg_helper(self.spec.value, val_range, addr, data);
    }
}

/// Macro helps to build a static register.
#[macro_export]
macro_rules! static_register {
    (ty: $ty:ty,offset: $offset:expr,value: $value:expr,) => {{
        use crate::register_space::*;
        static REG_SPEC: StaticRegisterSpec<$ty> = StaticRegisterSpec::<$ty> {
            offset: $offset,
            value: $value,
        };
        StaticRegister::new(&REG_SPEC)
    }};
}

/// Spec for a regular register. It specifies it's location on register space, guest writable mask
/// and guest write to clear mask.
pub struct RegisterSpec<T> {
    pub name: String,
    pub offset: RegisterOffset,
    pub reset_value: T,
    /// Only masked bits could be written by guest.
    pub guest_writeable_mask: T,
    /// When write 1 to bits masked, those bits will be cleared. See Xhci spec 5.1
    /// for more details.
    pub guest_write_1_to_clear_mask: T,
}

struct RegisterInner<T: RegisterValue> {
    spec: RegisterSpec<T>,
    value: T,
    write_cb: Option<Box<dyn Fn(T) -> T + Send>>,
}

/// Register is a thread safe struct. It can be safely changed from any thread.
#[derive(Clone)]
pub struct Register<T: RegisterValue> {
    inner: Arc<Mutex<RegisterInner<T>>>,
}

impl<T: RegisterValue> Register<T> {
    pub fn new(spec: RegisterSpec<T>, val: T) -> Self {
        Register {
            inner: Arc::new(Mutex::new(RegisterInner {
                spec,
                value: val,
                write_cb: None,
            })),
        }
    }

    fn lock(&self) -> MutexGuard<RegisterInner<T>> {
        self.inner.lock()
    }
}

// All functions implemented on this one is thread safe.
impl<T: RegisterValue> RegisterInterface for Register<T> {
    fn range(&self) -> RegisterRange {
        let locked = self.lock();
        let spec = &locked.spec;
        RegisterRange {
            from: spec.offset,
            to: spec.offset + (size_of::<T>() as u64) - 1,
        }
    }

    fn read(&self, addr: RegisterOffset, data: &mut [u8]) {
        let val_range = self.range();
        let value = self.lock().value;
        read_reg_helper(value, val_range, addr, data);
    }

    fn write(&self, addr: RegisterOffset, data: &[u8]) {
        let my_range = self.range();
        let write_range = RegisterRange {
            from: addr,
            to: addr + data.len() as u64 - 1,
        };

        let overlap = match my_range.overlap_range(&write_range) {
            Some(range) => range,
            None => {
                error!("write should not be invoked on this register");
                return;
            }
        };
        let my_start_idx = (overlap.from - my_range.from) as usize;
        let write_start_idx = (overlap.from - write_range.from) as usize;
        let total_size = (overlap.to - overlap.from) as usize + 1;

        let mut reg_value: T = self.lock().value;
        let value: &mut [u8] = reg_value.as_mut_slice();
        for i in 0..total_size {
            value[my_start_idx + i] = self.apply_write_masks_to_byte(
                value[my_start_idx + i],
                data[write_start_idx + i],
                my_start_idx + i,
            );
        }

        // A single u64 register is done by write to lower 32 bit and then higher 32 bit. Callback
        // should only be invoked when higher is written.
        if my_range.to != overlap.to {
            self.lock().value = reg_value;
            return;
        }

        // Taking the callback out of register when executing it. This prevent dead lock if
        // callback want to read current register value.
        // Note that the only source of callback comes from mmio writing, which is synchronized.
        let cb = {
            let mut inner = self.lock();
            match inner.write_cb.take() {
                Some(cb) => cb,
                None => {
                    // Write value if there is no callback.
                    inner.value = reg_value;
                    return;
                }
            }
        };
        // Callback is invoked without holding any lock.
        let value = cb(reg_value);
        let mut inner = self.lock();
        inner.value = value;
        inner.write_cb = Some(cb);
    }

    fn reset(&self) {
        let mut locked = self.lock();
        locked.value = locked.spec.reset_value;
    }
}

impl<T: RegisterValue> Register<T> {
    /// Get current value of this register.
    pub fn get_value(&self) -> T {
        self.lock().value
    }

    /// This function apply "write 1 to clear mask" and "guest writeable mask".
    /// All write operations should go through this, the result of this function
    /// is the new state of correspoding byte.
    pub fn apply_write_masks_to_byte(&self, old_byte: u8, write_byte: u8, offset: usize) -> u8 {
        let locked = self.lock();
        let spec = &locked.spec;
        let guest_write_1_to_clear_mask: u64 = spec.guest_write_1_to_clear_mask.into();
        let guest_writeable_mask: u64 = spec.guest_writeable_mask.into();
        // Mask with w1c mask.
        let w1c_mask = (guest_write_1_to_clear_mask >> (offset * 8)) as u8;
        let val = (!w1c_mask & write_byte) | (w1c_mask & old_byte & !write_byte);
        // Mask with writable mask.
        let w_mask = (guest_writeable_mask >> (offset * 8)) as u8;
        (old_byte & (!w_mask)) | (val & w_mask)
    }

    /// Set a callback. It will be invoked when write happens.
    pub fn set_write_cb<C: 'static + Fn(T) -> T + Send>(&self, callback: C) {
        self.lock().write_cb = Some(Box::new(callback));
    }

    /// Set value from device side. Callback won't be invoked.
    pub fn set_value(&self, val: T) {
        self.lock().value = val;
    }

    /// Set masked bits.
    pub fn set_bits(&self, mask: T) {
        self.lock().value.set_bits(mask);
    }

    /// Clear masked bits.
    pub fn clear_bits(&self, mask: T) {
        self.lock().value.clear_bits(mask);
    }
}

#[macro_export]
macro_rules! register {
    (
        name: $name:tt,
        ty: $ty:ty,
        offset: $offset:expr,
        reset_value: $rv:expr,
        guest_writeable_mask: $mask:expr,
        guest_write_1_to_clear_mask: $w1tcm:expr,
    ) => {{
        use crate::register_space::*;
        let spec: RegisterSpec<$ty> = RegisterSpec::<$ty> {
            name: String::from($name),
            offset: $offset,
            reset_value: $rv,
            guest_writeable_mask: $mask,
            guest_write_1_to_clear_mask: $w1tcm,
        };
        Register::<$ty>::new(spec, $rv)
    }};
    (name: $name:tt, ty: $ty:ty,offset: $offset:expr,reset_value: $rv:expr,) => {{
        use crate::register_space::*;
        let spec: RegisterSpec<$ty> = RegisterSpec::<$ty> {
            name: String::from($name),
            offset: $offset,
            reset_value: $rv,
            guest_writeable_mask: !0,
            guest_write_1_to_clear_mask: 0,
        };
        Register::<$ty>::new(spec, $rv)
    }};
}

#[macro_export]
macro_rules! register_array {
    (
        name: $name:tt,
        ty:
        $ty:ty,cnt:
        $cnt:expr,base_offset:
        $base_offset:expr,stride:
        $stride:expr,reset_value:
        $rv:expr,guest_writeable_mask:
        $gwm:expr,guest_write_1_to_clear_mask:
        $gw1tcm:expr,
    ) => {{
        use crate::register_space::*;
        let mut v: Vec<Register<$ty>> = Vec::new();
        for i in 0..$cnt {
            let offset = $base_offset + ($stride * i) as RegisterOffset;
            let spec: RegisterSpec<$ty> = RegisterSpec::<$ty> {
                name: format!("{}-{}", $name, i),
                offset,
                reset_value: $rv,
                guest_writeable_mask: $gwm,
                guest_write_1_to_clear_mask: $gw1tcm,
            };
            v.push(Register::<$ty>::new(spec, $rv));
        }
        v
    }};
}

#[cfg(test)]
mod tests {
    use super::*;

    static REG_SPEC0: StaticRegisterSpec<u8> = StaticRegisterSpec::<u8> {
        offset: 3,
        value: 32,
    };

    static REG_SPEC1: StaticRegisterSpec<u16> = StaticRegisterSpec::<u16> {
        offset: 3,
        value: 32,
    };

    #[test]
    fn static_register_basic_test_u8() {
        let r = StaticRegister::<u8> { spec: &REG_SPEC0 };
        let mut data: [u8; 4] = [0, 0, 0, 0];
        assert_eq!(r.range().from, 3);
        assert_eq!(r.range().to, 3);
        r.read(0, &mut data);
        assert_eq!(data, [0, 0, 0, 32]);
        r.read(2, &mut data);
        assert_eq!(data, [0, 32, 0, 32]);
    }

    #[test]
    fn static_register_basic_test_u16() {
        let r = StaticRegister::<u16> { spec: &REG_SPEC1 };
        let mut data: [u8; 4] = [0, 0, 0, 0];
        assert_eq!(r.range().from, 3);
        assert_eq!(r.range().to, 4);
        r.read(0, &mut data);
        assert_eq!(data, [0, 0, 0, 32]);
        r.read(2, &mut data);
        assert_eq!(data, [0, 32, 0, 32]);
    }

    #[test]
    fn static_register_interface_test() {
        let r: Box<dyn RegisterInterface> = Box::new(static_register! {
            ty: u8,
            offset: 3,
            value: 32,
        });
        let mut data: [u8; 4] = [0, 0, 0, 0];
        assert_eq!(r.range().from, 3);
        assert_eq!(r.range().to, 3);
        r.read(0, &mut data);
        assert_eq!(data, [0, 0, 0, 32]);
        r.read(2, &mut data);
        assert_eq!(data, [0, 32, 0, 32]);
    }

    #[test]
    fn register_basic_rw_test() {
        let r = register! {
            name: "",
            ty: u8,
            offset: 3,
            reset_value: 0xf1,
            guest_writeable_mask: 0xff,
            guest_write_1_to_clear_mask: 0x0,
        };
        let mut data: [u8; 4] = [0, 0, 0, 0];
        assert_eq!(r.range().from, 3);
        assert_eq!(r.range().to, 3);
        r.read(0, &mut data);
        assert_eq!(data, [0, 0, 0, 0xf1]);
        r.read(2, &mut data);
        assert_eq!(data, [0, 0xf1, 0, 0xf1]);
        data = [0, 0, 0, 0xab];
        r.write(0, &data);
        assert_eq!(r.get_value(), 0xab);
        r.reset();
        assert_eq!(r.get_value(), 0xf1);
        r.set_value(0xcc);
        assert_eq!(r.get_value(), 0xcc);
    }

    #[test]
    fn register_basic_writeable_mask_test() {
        let r = register! {
            name: "",
            ty: u8,
            offset: 3,
            reset_value: 0x0,
            guest_writeable_mask: 0xf,
            guest_write_1_to_clear_mask: 0x0,
        };
        let mut data: [u8; 4] = [0, 0, 0, 0];
        assert_eq!(r.range().from, 3);
        assert_eq!(r.range().to, 3);
        r.read(0, &mut data);
        assert_eq!(data, [0, 0, 0, 0]);
        data = [0, 0, 0, 0xab];
        r.write(0, &data);
        assert_eq!(r.get_value(), 0x0b);
        r.reset();
        assert_eq!(r.get_value(), 0x0);
        r.set_value(0xcc);
        assert_eq!(r.get_value(), 0xcc);
    }

    #[test]
    fn register_basic_write_1_to_clear_mask_test() {
        let r = register! {
            name: "",
            ty: u8,
            offset: 3,
            reset_value: 0xf1,
            guest_writeable_mask: 0xff,
            guest_write_1_to_clear_mask: 0xf0,
        };
        let mut data: [u8; 4] = [0, 0, 0, 0];
        assert_eq!(r.range().from, 3);
        assert_eq!(r.range().to, 3);
        r.read(0, &mut data);
        assert_eq!(data, [0, 0, 0, 0xf1]);
        data = [0, 0, 0, 0xfa];
        r.write(0, &data);
        assert_eq!(r.get_value(), 0x0a);
        r.reset();
        assert_eq!(r.get_value(), 0xf1);
        r.set_value(0xcc);
        assert_eq!(r.get_value(), 0xcc);
    }

    #[test]
    fn register_basic_write_1_to_clear_mask_test_u32() {
        let r = register! {
            name: "",
            ty: u32,
            offset: 0,
            reset_value: 0xfff1,
            guest_writeable_mask: 0xff,
            guest_write_1_to_clear_mask: 0xf0,
        };
        let mut data: [u8; 4] = [0, 0, 0, 0];
        assert_eq!(r.range().from, 0);
        assert_eq!(r.range().to, 3);
        r.read(0, &mut data);
        assert_eq!(data, [0xf1, 0xff, 0, 0]);
        data = [0xfa, 0, 0, 0];
        r.write(0, &data);
        assert_eq!(r.get_value(), 0xff0a);
        r.reset();
        assert_eq!(r.get_value(), 0xfff1);
        r.set_value(0xcc);
        assert_eq!(r.get_value(), 0xcc);
    }

    #[test]
    fn register_callback_test() {
        let state = Arc::new(Mutex::new(0u8));
        let r = register! {
            name: "",
            ty: u8,
            offset: 3,
            reset_value: 0xf1,
            guest_writeable_mask: 0xff,
            guest_write_1_to_clear_mask: 0xf0,
        };

        let s2 = state.clone();
        r.set_write_cb(move |val: u8| {
            *s2.lock() = val as u8;
            val
        });
        let data: [u8; 4] = [0, 0, 0, 0xff];
        r.write(0, &data);
        assert_eq!(*state.lock(), 0xf);
        r.set_value(0xab);
        assert_eq!(*state.lock(), 0xf);
        let data: [u8; 1] = [0xfc];
        r.write(3, &data);
        assert_eq!(*state.lock(), 0xc);
    }
}