mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-12-25 04:14:06 +00:00
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:
parent
f6752e7927
commit
212b45f77b
4 changed files with 885 additions and 0 deletions
5
devices/src/usb/mod.rs
Normal file
5
devices/src/usb/mod.rs
Normal 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;
|
577
devices/src/usb/xhci/mmio_register.rs
Normal file
577
devices/src/usb/xhci/mmio_register.rs
Normal 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(®_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: ®_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: ®_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);
|
||||
}
|
||||
}
|
296
devices/src/usb/xhci/mmio_space.rs
Normal file
296
devices/src/usb/xhci/mmio_space.rs
Normal 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(®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);
|
||||
}
|
||||
|
||||
}
|
7
devices/src/usb/xhci/mod.rs
Normal file
7
devices/src/usb/xhci/mod.rs
Normal 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;
|
Loading…
Reference in a new issue