usb: Add mmio space.

Add mmio register and mmio space.

BUG=chromium:831850
TEST=cargo test

Change-Id: I8091fc211183d9ec8d10dc4089206a71b2724ad0
Reviewed-on: https://chromium-review.googlesource.com/1142243
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Jingkui Wang <jkwang@google.com>
Reviewed-by: David Tolnay <dtolnay@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
Jingkui Wang 2018-07-17 10:41:06 -07:00 committed by chrome-bot
parent f6752e7927
commit 212b45f77b
4 changed files with 885 additions and 0 deletions

5
devices/src/usb/mod.rs Normal file
View file

@ -0,0 +1,5 @@
// 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.
pub mod xhci;

View file

@ -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);
}
}

View file

@ -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::mmio_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::*;
use std::sync::{Arc, Mutex};
use usb::xhci::mmio_register::{RegisterSpec, StaticRegister, StaticRegisterSpec};
#[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);
}
}

View file

@ -0,0 +1,7 @@
// 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 mmio_register;
mod mmio_space;