summary refs log tree commit diff
path: root/devices/src/register_space
diff options
context:
space:
mode:
authorJingkui Wang <jkwang@google.com>2019-03-06 22:36:25 -0800
committerchrome-bot <chrome-bot@chromium.org>2019-03-08 21:20:19 -0800
commitb771521aa653d2275ffff1b36c32a204fd8ad1a5 (patch)
tree887fbd139c60d413dbbf671c85dde63304186d17 /devices/src/register_space
parent699170b6b74b8d03e109911f866ab23854b63e20 (diff)
downloadcrosvm-b771521aa653d2275ffff1b36c32a204fd8ad1a5.tar
crosvm-b771521aa653d2275ffff1b36c32a204fd8ad1a5.tar.gz
crosvm-b771521aa653d2275ffff1b36c32a204fd8ad1a5.tar.bz2
crosvm-b771521aa653d2275ffff1b36c32a204fd8ad1a5.tar.lz
crosvm-b771521aa653d2275ffff1b36c32a204fd8ad1a5.tar.xz
crosvm-b771521aa653d2275ffff1b36c32a204fd8ad1a5.tar.zst
crosvm-b771521aa653d2275ffff1b36c32a204fd8ad1a5.zip
Move and rename mmio_register to devices/src
A later CL will rename structs.

BUG=chromium:831850
TEST=local build

Change-Id: I8cf70da16918035a6fad2e925f91a315693ff1ed
Reviewed-on: https://chromium-review.googlesource.com/1506828
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Jingkui Wang <jkwang@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'devices/src/register_space')
-rw-r--r--devices/src/register_space/mod.rs10
-rw-r--r--devices/src/register_space/register.rs577
-rw-r--r--devices/src/register_space/register_space.rs296
3 files changed, 883 insertions, 0 deletions
diff --git a/devices/src/register_space/mod.rs b/devices/src/register_space/mod.rs
new file mode 100644
index 0000000..58d35e3
--- /dev/null
+++ b/devices/src/register_space/mod.rs
@@ -0,0 +1,10 @@
+// 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.
+
+#[macro_use]
+mod register;
+mod register_space;
+
+pub use self::register::*;
+pub use self::register_space::*;
diff --git a/devices/src/register_space/register.rs b/devices/src/register_space/register.rs
new file mode 100644
index 0000000..3b113f9
--- /dev/null
+++ b/devices/src/register_space/register.rs
@@ -0,0 +1,577 @@
+// 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;
+use std::boxed::Box;
+use std::cmp::{max, min, Ord, Ordering, PartialEq, PartialOrd};
+use std::mem::size_of;
+use std::sync::{Arc, Mutex, MutexGuard};
+
+use data_model::DataInit;
+
+/// Type of offset in the bar.
+pub type BarOffset = u64;
+
+/// This represents a range of memory in the MMIO space starting from Bar.
+/// Both from and to are inclusive.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct BarRange {
+    pub from: BarOffset,
+    pub to: BarOffset,
+}
+
+impl Ord for BarRange {
+    fn cmp(&self, other: &BarRange) -> Ordering {
+        self.from.cmp(&other.from)
+    }
+}
+
+impl PartialOrd for BarRange {
+    fn partial_cmp(&self, other: &BarRange) -> Option<Ordering> {
+        self.from.partial_cmp(&other.from)
+    }
+}
+
+impl BarRange {
+    /// Return true if those range overlaps.
+    pub fn overlap_with(&self, other: &BarRange) -> bool {
+        !(self.from > other.to || self.to < other.from)
+    }
+
+    /// Get the overlapping part of two BarRange.
+    /// 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: &BarRange) -> Option<BarRange> {
+        if !self.overlap_with(other) {
+            return None;
+        }
+        Some(BarRange {
+            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.clone() | mask;
+    }
+    // Clear masked bits.
+    fn clear_bits(&mut self, mask: Self) {
+        *self = self.clone() & (!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: BarRange,
+    addr: BarOffset,
+    data: &mut [u8],
+) {
+    let read_range = BarRange {
+        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 {
+    /// Bar range of this register.
+    fn bar_range(&self) -> BarRange;
+    /// Handle read bar.
+    fn read_bar(&self, addr: BarOffset, data: &mut [u8]);
+    /// Handle write bar.
+    fn write_bar(&self, _addr: BarOffset, _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: BarOffset,
+    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 bar_range(&self) -> BarRange {
+        BarRange {
+            from: self.spec.offset,
+            to: self.spec.offset + (size_of::<T>() as u64) - 1,
+        }
+    }
+
+    fn read_bar(&self, addr: BarOffset, data: &mut [u8]) {
+        let val_range = self.bar_range();
+        read_reg_helper(self.spec.value.clone(), 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,) => {{
+        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 bar, guest writable mask and guest
+/// write to clear mask.
+pub struct RegisterSpec<T> {
+    pub name: String,
+    pub offset: BarOffset,
+    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<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().expect("fail to lock register")
+    }
+}
+
+// All functions implemented on this one is thread safe.
+impl<T: RegisterValue> RegisterInterface for Register<T> {
+    fn bar_range(&self) -> BarRange {
+        let locked = self.lock();
+        let spec = &locked.spec;
+        BarRange {
+            from: spec.offset,
+            to: spec.offset + (size_of::<T>() as u64) - 1,
+        }
+    }
+
+    fn read_bar(&self, addr: BarOffset, data: &mut [u8]) {
+        let val_range = self.bar_range();
+        let value = self.lock().value.clone();
+        read_reg_helper(value, val_range, addr, data);
+    }
+
+    fn write_bar(&self, addr: BarOffset, data: &[u8]) {
+        let my_range = self.bar_range();
+        let write_range = BarRange {
+            from: addr,
+            to: addr + data.len() as u64 - 1,
+        };
+
+        let overlap = match my_range.overlap_range(&write_range) {
+            Some(range) => range,
+            None => {
+                error!("write bar 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.clone();
+        {
+            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,
+                );
+            }
+        }
+        // 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.clone();
+    }
+}
+
+impl<T: RegisterValue> Register<T> {
+    /// Get current value of this register.
+    pub fn get_value(&self) -> T {
+        self.lock().value.clone()
+    }
+
+    /// 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.clone().into();
+        let guest_writeable_mask: u64 = spec.guest_writeable_mask.clone().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 bar 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,
+    ) => {{
+        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,) => {{
+        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,
+    ) => {{
+        let mut v: Vec<Register<$ty>> = Vec::new();
+        for i in 0..$cnt {
+            let offset = $base_offset + ($stride * i) as BarOffset;
+            let mut 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.bar_range().from, 3);
+        assert_eq!(r.bar_range().to, 3);
+        r.read_bar(0, &mut data);
+        assert_eq!(data, [0, 0, 0, 32]);
+        r.read_bar(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.bar_range().from, 3);
+        assert_eq!(r.bar_range().to, 4);
+        r.read_bar(0, &mut data);
+        assert_eq!(data, [0, 0, 0, 32]);
+        r.read_bar(2, &mut data);
+        assert_eq!(data, [0, 32, 0, 32]);
+    }
+
+    #[test]
+    fn static_register_interface_test() {
+        let r: Box<RegisterInterface> = Box::new(static_register! {
+            ty: u8,
+            offset: 3,
+            value: 32,
+        });
+        let mut data: [u8; 4] = [0, 0, 0, 0];
+        assert_eq!(r.bar_range().from, 3);
+        assert_eq!(r.bar_range().to, 3);
+        r.read_bar(0, &mut data);
+        assert_eq!(data, [0, 0, 0, 32]);
+        r.read_bar(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.bar_range().from, 3);
+        assert_eq!(r.bar_range().to, 3);
+        r.read_bar(0, &mut data);
+        assert_eq!(data, [0, 0, 0, 0xf1]);
+        r.read_bar(2, &mut data);
+        assert_eq!(data, [0, 0xf1, 0, 0xf1]);
+        data = [0, 0, 0, 0xab];
+        r.write_bar(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.bar_range().from, 3);
+        assert_eq!(r.bar_range().to, 3);
+        r.read_bar(0, &mut data);
+        assert_eq!(data, [0, 0, 0, 0]);
+        data = [0, 0, 0, 0xab];
+        r.write_bar(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.bar_range().from, 3);
+        assert_eq!(r.bar_range().to, 3);
+        r.read_bar(0, &mut data);
+        assert_eq!(data, [0, 0, 0, 0xf1]);
+        data = [0, 0, 0, 0xfa];
+        r.write_bar(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.bar_range().from, 0);
+        assert_eq!(r.bar_range().to, 3);
+        r.read_bar(0, &mut data);
+        assert_eq!(data, [0xf1, 0xff, 0, 0]);
+        data = [0xfa, 0, 0, 0];
+        r.write_bar(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().unwrap() = val as u8;
+            val
+        });
+        let data: [u8; 4] = [0, 0, 0, 0xff];
+        r.write_bar(0, &data);
+        assert_eq!(*state.lock().unwrap(), 0xf);
+        r.set_value(0xab);
+        assert_eq!(*state.lock().unwrap(), 0xf);
+        let data: [u8; 1] = [0xfc];
+        r.write_bar(3, &data);
+        assert_eq!(*state.lock().unwrap(), 0xc);
+    }
+}
diff --git a/devices/src/register_space/register_space.rs b/devices/src/register_space/register_space.rs
new file mode 100644
index 0000000..a8e856b
--- /dev/null
+++ b/devices/src/register_space/register_space.rs
@@ -0,0 +1,296 @@
+// 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 super::register::{BarOffset, BarRange, Register, RegisterInterface, RegisterValue};
+use std::collections::btree_map::BTreeMap;
+
+/// MMIO space repesents a set of registers. It can handle bar read/write operations.
+pub struct MMIOSpace {
+    regs: BTreeMap<BarRange, Box<RegisterInterface>>,
+}
+
+impl MMIOSpace {
+    /// Creates a new empty MMIOSpace.
+    pub fn new() -> MMIOSpace {
+        MMIOSpace {
+            regs: BTreeMap::new(),
+        }
+    }
+
+    /// Add a register to MMIO space.
+    pub fn add_register<T: RegisterInterface + 'static>(&mut self, reg: T) {
+        let range = reg.bar_range();
+        debug_assert!(self.get_register(range.from).is_none());
+        if cfg!(debug_assertions) {
+            if let Some(r) = self.first_before(range.to) {
+                debug_assert!(r.bar_range().to < range.to);
+            }
+        }
+
+        let insert_result = self.regs.insert(range, Box::new(reg)).is_none();
+        debug_assert!(insert_result);
+    }
+
+    /// Add an array of registers.
+    pub fn add_register_array<T: RegisterValue>(&mut self, regs: &[Register<T>]) {
+        for r in regs {
+            self.add_register(r.clone());
+        }
+    }
+
+    /// Read bar range.
+    pub fn read_bar(&self, addr: BarOffset, data: &mut [u8]) {
+        let mut current_addr: BarOffset = addr;
+        while current_addr < addr + data.len() as BarOffset {
+            if let Some(r) = self.get_register(current_addr) {
+                // Next addr to read is.
+                current_addr = r.bar_range().to + 1;
+                r.read_bar(addr, data);
+            } else {
+                // TODO(jkwang) Add logging for debug here.
+                current_addr = current_addr + 1;
+            }
+        }
+    }
+
+    /// Write bar range. If the targeted register has a callback, it will be invoked with the new
+    /// value.
+    pub fn write_bar(&self, addr: BarOffset, data: &[u8]) {
+        let mut current_addr: BarOffset = addr;
+        while current_addr < addr + data.len() as BarOffset {
+            if let Some(r) = self.get_register(current_addr) {
+                // Next addr to read is, bar_range is inclusive.
+                current_addr = r.bar_range().to + 1;
+                r.write_bar(addr, data);
+            } else {
+                current_addr = current_addr + 1;
+            }
+        }
+    }
+
+    /// Get first register before this addr.
+    fn first_before(&self, addr: BarOffset) -> Option<&Box<RegisterInterface>> {
+        for (range, r) in self.regs.iter().rev() {
+            if range.from <= addr {
+                return Some(r);
+            }
+        }
+        None
+    }
+
+    /// Get register at this addr.
+    fn get_register(&self, addr: BarOffset) -> Option<&Box<RegisterInterface>> {
+        let r = self.first_before(addr)?;
+        let range = r.bar_range();
+        if addr <= range.to {
+            Some(r)
+        } else {
+            None
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::super::{RegisterSpec, StaticRegister, StaticRegisterSpec};
+    use super::*;
+    use std::sync::{Arc, Mutex};
+
+    #[test]
+    fn mmio_no_reg() {
+        let mmio = MMIOSpace::new();
+        let mut data: [u8; 4] = [4, 3, 2, 1];
+        // Read bar should be no op cause no register.
+        mmio.read_bar(0, &mut data);
+        assert_eq!([4, 3, 2, 1], data);
+        // Write bar should be no op.
+        mmio.write_bar(0, &[0, 0, 0, 0]);
+        mmio.read_bar(0, &mut data);
+        assert_eq!([4, 3, 2, 1], data);
+    }
+
+    #[test]
+    #[should_panic]
+    fn mmio_reg_overlap() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register(static_register!(
+        ty: u32,
+        offset: 4,
+        value: 11,
+        ));
+
+        mmio.add_register(static_register!(
+        ty: u16,
+        offset: 7,
+        value: 11,
+        ));
+    }
+
+    #[test]
+    fn mmio_static_reg() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register(static_register!(
+            ty: u8,
+            offset: 0,
+            value: 11,
+        ));
+        let mut data: [u8; 4] = [4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([11, 3, 2, 1], data);
+        // Write bar should be no op.
+        mmio.write_bar(0, &[0, 0, 0, 0]);
+        let mut data: [u8; 4] = [4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([11, 3, 2, 1], data);
+    }
+
+    #[test]
+    fn mmio_static_reg_offset() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register(static_register!(
+            ty: u32,
+            offset: 2,
+            value: 0xaabbccdd,
+        ));
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+        // Write bar should be no op.
+        mmio.write_bar(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+    }
+
+    #[test]
+    fn mmio_reg_write() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register(register!(
+            name: "",
+            ty: u32,
+            offset: 2,
+            reset_value: 0xaabbccdd,
+        ));
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+        mmio.write_bar(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0, 0, 0, 0, 2, 1], data);
+    }
+
+    #[test]
+    fn mmio_reg_writeable() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register(register!(
+            name: "",
+            ty: u32,
+            offset: 2,
+            reset_value: 0xaabbccdd,
+            guest_writeable_mask: 0x00f0000f,
+            guest_write_1_to_clear_mask: 0,
+        ));
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+        mmio.write_bar(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xd0, 0xcc, 0x0b, 0xaa, 2, 1], data);
+    }
+
+    #[test]
+    fn mmio_reg_writeable_callback() {
+        let state = Arc::new(Mutex::new(0u32));
+        let mut mmio = MMIOSpace::new();
+        let reg = register!(
+            name: "",
+            ty: u32,
+            offset: 2,
+            reset_value: 0xaabbccdd,
+            guest_writeable_mask: 0x00f0000f,
+            guest_write_1_to_clear_mask: 0,
+        );
+        mmio.add_register(reg.clone());
+        let state_clone = state.clone();
+        reg.set_write_cb(move |val: u32| {
+            *state_clone.lock().unwrap() = val;
+            val
+        });
+
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+        mmio.write_bar(0, &[0, 0, 0, 0, 0, 0, 0, 0]);
+        assert_eq!(0xaa0bccd0, *state.lock().unwrap());
+    }
+
+    #[test]
+    fn mmio_reg_write_to_clear() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register(register!(
+        name: "",
+        ty: u32,
+        offset: 2,
+        reset_value: 0xaabbccdd,
+        guest_writeable_mask: 0xfff0000f,
+        guest_write_1_to_clear_mask: 0xf0000000,
+        ));
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data);
+        mmio.write_bar(0, &[0, 0, 0, 0, 0, 0xad, 0, 0]);
+        let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1];
+        mmio.read_bar(0, &mut data);
+        assert_eq!([8, 7, 0xd0, 0xcc, 0x0b, 0x0d, 2, 1], data);
+    }
+
+    #[test]
+    fn mmio_reg_array() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register_array(&register_array!(
+            name: "",
+            ty: u8,
+            cnt: 8,
+            base_offset: 10,
+            stride: 2,
+            reset_value: 0xff,
+            guest_writeable_mask: !0,
+            guest_write_1_to_clear_mask: 0,
+        ));
+        let mut data: [u8; 8] = [0; 8];
+        mmio.read_bar(8, &mut data);
+        assert_eq!([0, 0, 0xff, 0, 0xff, 0, 0xff, 0], data);
+    }
+
+    #[test]
+    fn mmio_reg_multi_array() {
+        let mut mmio = MMIOSpace::new();
+        mmio.add_register_array(&register_array!(
+        name: "",
+        ty: u8,
+        cnt: 8,
+        base_offset: 10,
+        stride: 2,
+        reset_value: 0xff,
+        guest_writeable_mask: !0,
+        guest_write_1_to_clear_mask: 0,
+        ));
+        mmio.add_register_array(&register_array!(
+        name: "",
+        ty: u8,
+        cnt: 8,
+        base_offset: 11,
+        stride: 2,
+        reset_value: 0xee,
+        guest_writeable_mask: !0,
+        guest_write_1_to_clear_mask: 0,
+        ));
+        let mut data: [u8; 8] = [0; 8];
+        mmio.read_bar(8, &mut data);
+        assert_eq!([0, 0, 0xff, 0xee, 0xff, 0xee, 0xff, 0xee], data);
+    }
+
+}