diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs new file mode 100644 index 0000000000..a4466418c0 --- /dev/null +++ b/devices/src/usb/mod.rs @@ -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; diff --git a/devices/src/usb/xhci/mmio_register.rs b/devices/src/usb/xhci/mmio_register.rs new file mode 100644 index 0000000000..afa4fa8ae9 --- /dev/null +++ b/devices/src/usb/xhci/mmio_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 { + 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 { + 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 + + Clone + + DataInit + + std::ops::BitOr + + std::ops::BitAnd + + std::ops::Not + + 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( + 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 { + 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 +where + T: RegisterValue, +{ + spec: &'static StaticRegisterSpec, +} + +impl StaticRegister +where + T: RegisterValue, +{ + /// Create an new static register from spec. + pub fn new(spec: &'static StaticRegisterSpec) -> StaticRegister { + StaticRegister { spec } + } +} + +impl RegisterInterface for StaticRegister +where + T: RegisterValue, +{ + fn bar_range(&self) -> BarRange { + BarRange { + from: self.spec.offset, + to: self.spec.offset + (size_of::() 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(®_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 { + 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 { + spec: RegisterSpec, + value: T, + write_cb: Option T + Send>>, +} + +/// Register is a thread safe struct. It can be safely changed from any thread. +#[derive(Clone)] +pub struct Register { + inner: Arc>>, +} + +impl Register { + pub fn new(spec: RegisterSpec, val: T) -> Self { + Register { + inner: Arc::new(Mutex::new(RegisterInner { + spec, + value: val, + write_cb: None, + })), + } + } + + fn lock(&self) -> MutexGuard> { + self.inner.lock().expect("fail to lock register") + } +} + +// All functions implemented on this one is thread safe. +impl RegisterInterface for Register { + fn bar_range(&self) -> BarRange { + let locked = self.lock(); + let spec = &locked.spec; + BarRange { + from: spec.offset, + to: spec.offset + (size_of::() 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 Register { + /// 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 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> = 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 = StaticRegisterSpec:: { + offset: 3, + value: 32, + }; + + static REG_SPEC1: StaticRegisterSpec = StaticRegisterSpec:: { + offset: 3, + value: 32, + }; + + #[test] + fn static_register_basic_test_u8() { + let r = StaticRegister:: { spec: ®_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:: { spec: ®_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 = 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/usb/xhci/mmio_space.rs b/devices/src/usb/xhci/mmio_space.rs new file mode 100644 index 0000000000..b30c1e7ea8 --- /dev/null +++ b/devices/src/usb/xhci/mmio_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::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>, +} + +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(&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(&mut self, regs: &[Register]) { + 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> { + 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> { + 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(®ister_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(®ister_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(®ister_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); + } + +} diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs new file mode 100644 index 0000000000..ca4d7fdee3 --- /dev/null +++ b/devices/src/usb/xhci/mod.rs @@ -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;