// 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::{Register, RegisterInterface, RegisterOffset, RegisterRange, RegisterValue}; use std::collections::btree_map::BTreeMap; /// Register space repesents a set of registers. It can handle read/write operations. pub struct RegisterSpace { regs: BTreeMap>, } impl RegisterSpace { /// Creates a new empty RegisterSpace. pub fn new() -> RegisterSpace { RegisterSpace { regs: BTreeMap::new(), } } /// Add a register to register space. pub fn add_register(&mut self, reg: T) { let range = reg.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.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 range. pub fn read(&self, addr: RegisterOffset, data: &mut [u8]) { let mut current_addr: RegisterOffset = addr; while current_addr < addr + data.len() as RegisterOffset { if let Some(r) = self.get_register(current_addr) { // Next addr to read is. current_addr = r.range().to + 1; r.read(addr, data); } else { // TODO(jkwang) Add logging for debug here. current_addr += 1; } } } /// Write range. If the targeted register has a callback, it will be invoked with the new /// value. pub fn write(&self, addr: RegisterOffset, data: &[u8]) { let mut current_addr: RegisterOffset = addr; while current_addr < addr + data.len() as RegisterOffset { if let Some(r) = self.get_register(current_addr) { // Next addr to read is, range is inclusive. current_addr = r.range().to + 1; r.write(addr, data); } else { current_addr += 1; } } } /// Get first register before this addr. fn first_before(&self, addr: RegisterOffset) -> Option<&dyn RegisterInterface> { for (range, r) in self.regs.iter().rev() { if range.from <= addr { return Some(r.as_ref()); } } None } /// Get register at this addr. fn get_register(&self, addr: RegisterOffset) -> Option<&dyn RegisterInterface> { let r = self.first_before(addr)?; let range = r.range(); if addr <= range.to { Some(r) } else { None } } } #[cfg(test)] mod tests { use super::*; use std::sync::Arc; use sync::Mutex; #[test] fn regs_no_reg() { let regs = RegisterSpace::new(); let mut data: [u8; 4] = [4, 3, 2, 1]; // Read should be no op cause no register. regs.read(0, &mut data); assert_eq!([4, 3, 2, 1], data); // Write should be no op. regs.write(0, &[0, 0, 0, 0]); regs.read(0, &mut data); assert_eq!([4, 3, 2, 1], data); } #[test] #[should_panic] #[cfg(debug_assertions)] fn regs_reg_overlap() { let mut regs = RegisterSpace::new(); regs.add_register(static_register!( ty: u32, offset: 4, value: 11, )); regs.add_register(static_register!( ty: u16, offset: 7, value: 11, )); } #[test] fn regs_static_reg() { let mut regs = RegisterSpace::new(); regs.add_register(static_register!( ty: u8, offset: 0, value: 11, )); let mut data: [u8; 4] = [4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([11, 3, 2, 1], data); // Write should be no op. regs.write(0, &[0, 0, 0, 0]); let mut data: [u8; 4] = [4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([11, 3, 2, 1], data); } #[test] fn regs_static_reg_offset() { let mut regs = RegisterSpace::new(); regs.add_register(static_register!( ty: u32, offset: 2, value: 0xaabbccdd, )); let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data); // Write should be no op. regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]); let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data); } #[test] fn regs_reg_write() { let mut regs = RegisterSpace::new(); regs.add_register(register!( name: "", ty: u32, offset: 2, reset_value: 0xaabbccdd, )); let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data); regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]); let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([8, 7, 0, 0, 0, 0, 2, 1], data); } #[test] fn regs_reg_writeable() { let mut regs = RegisterSpace::new(); regs.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]; regs.read(0, &mut data); assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data); regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]); let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([8, 7, 0xd0, 0xcc, 0x0b, 0xaa, 2, 1], data); } #[test] fn regs_reg_writeable_callback() { let state = Arc::new(Mutex::new(0u32)); let mut regs = RegisterSpace::new(); let reg = register!( name: "", ty: u32, offset: 2, reset_value: 0xaabbccdd, guest_writeable_mask: 0x00f0000f, guest_write_1_to_clear_mask: 0, ); regs.add_register(reg.clone()); let state_clone = state.clone(); reg.set_write_cb(move |val: u32| { *state_clone.lock() = val; val }); let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data); regs.write(0, &[0, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(0xaa0bccd0, *state.lock()); } #[test] fn regs_reg_write_to_clear() { let mut regs = RegisterSpace::new(); regs.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]; regs.read(0, &mut data); assert_eq!([8, 7, 0xdd, 0xcc, 0xbb, 0xaa, 2, 1], data); regs.write(0, &[0, 0, 0, 0, 0, 0xad, 0, 0]); let mut data: [u8; 8] = [8, 7, 6, 5, 4, 3, 2, 1]; regs.read(0, &mut data); assert_eq!([8, 7, 0xd0, 0xcc, 0x0b, 0x0d, 2, 1], data); } #[test] fn regs_reg_array() { let mut regs = RegisterSpace::new(); regs.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]; regs.read(8, &mut data); assert_eq!([0, 0, 0xff, 0, 0xff, 0, 0xff, 0], data); } #[test] fn regs_reg_multi_array() { let mut regs = RegisterSpace::new(); regs.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, )); regs.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]; regs.read(8, &mut data); assert_eq!([0, 0, 0xff, 0xee, 0xff, 0xee, 0xff, 0xee], data); } }