| // Copyright 2020 The Fuchsia Authors | |
| // | |
| // Use of this source code is governed by a MIT-style | |
| // license that can be found in the LICENSE file or at | |
| // https://opensource.org/licenses/MIT | |
| //! ARM CoreSight component identification registers and constants, mirroring | |
| //! `<dev/coresight/component.h>`. | |
| use core::ffi::c_char; | |
| use core::fmt; | |
| use bitrs::{bitfield_repr, layout}; | |
| use regio::{AccessRestrictsTo, Accessible, MmioBank, MmioPtr, Offset, Ro}; | |
| use zr::static_assert; | |
| /// Typically components are 4KiB in size; the spec permits them to be larger. | |
| pub const MINIMUM_COMPONENT_SIZE: usize = 4096; | |
| /// Device architecture constants for ARM-designed components. | |
| pub mod arm { | |
| /// `DEVARCH.ARCHITECT`. | |
| pub const ARCHITECT: u16 = 0x23b; | |
| /// `DEVARCH.ARCHID` values. | |
| pub mod archid { | |
| /// Cross-Trigger Interface (CTI). | |
| pub const CTI: u16 = 0x1a14; | |
| /// Embedded Trace Macrocell (ETM) v3. | |
| pub const ETM3: u16 = 0x3a13; | |
| /// Embedded Trace Macrocell (ETM) v4. | |
| pub const ETM4: u16 = 0x4a13; | |
| /// Performance Monitors Extension (PMU) v2. | |
| pub const PMU2: u16 = 0x1a16; | |
| /// Performance Monitors Extension (PMU) v3. | |
| pub const PMU3: u16 = 0x2a16; | |
| /// Class 0x9 ROM Table. | |
| pub const ROM_TABLE: u16 = 0x0af7; | |
| /// ARM v8.0-A Core Debug Interface. | |
| pub const CORE_DEBUG_INTERFACE_8_0A: u16 = 0x6a15; | |
| /// ARM v8.1-A Core Debug Interface. | |
| pub const CORE_DEBUG_INTERFACE_8_1A: u16 = 0x7a15; | |
| /// ARM v8.2-A Core Debug Interface. | |
| pub const CORE_DEBUG_INTERFACE_8_2A: u16 = 0x8a15; | |
| } | |
| /// Peripheral part ID (`PARTID`) values. | |
| pub mod partid { | |
| /// Cross-Trigger Interface (SoC400 generation). | |
| pub const CTI_400: u16 = 0x0906; | |
| /// Cross-Trigger Interface (SoC600 generation). | |
| pub const CTI_600: u16 = 0x09ed; | |
| /// Embedded Trace Buffer (ETB). | |
| pub const ETB: u16 = 0x0907; | |
| /// Timestamp Generator. | |
| pub const TIMESTAMP_GENERATOR: u16 = 0x0101; | |
| /// Trace Memory Controller (TMC). | |
| pub const TMC: u16 = 0x0961; | |
| /// Trace Port Interface Unit (TPIU). | |
| pub const TPIU: u16 = 0x0912; | |
| /// Trace Funnel. | |
| pub const TRACE_FUNNEL: u16 = 0x0908; | |
| /// Trace Replicator. | |
| pub const TRACE_REPLICATOR: u16 = 0x0909; | |
| } | |
| } | |
| /// Component class identifier encoded in `CIDR1.CLASS` (`ComponentIdRegister::Class`). | |
| #[bitfield_repr(u8)] | |
| #[derive(Copy, Clone)] | |
| pub enum ClassId { | |
| /// Generic verification component (`0x0`). | |
| GenericVerification = 0x0, | |
| /// Class `0x1` ROM table (`0x1`). | |
| RomTable0x1 = 0x1, | |
| /// CoreSight component (`0x9`). | |
| CoreSight = 0x9, | |
| /// Peripheral test block (`0xb`). | |
| PeripheralTestBlock = 0xb, | |
| /// Generic IP component (`0xe`). | |
| GenericIp = 0xe, | |
| /// Non-standard component (`0xf`). For older components without standardized registers. | |
| NonStandard = 0xf, | |
| } | |
| impl ClassId { | |
| /// Returns the human-readable description of this component class. | |
| #[inline] | |
| pub const fn as_str(self) -> &'static str { | |
| class_id_to_string(self) | |
| } | |
| } | |
| impl fmt::Display for ClassId { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str(self.as_str()) | |
| } | |
| } | |
| static_assert!(size_of::<ClassId>() == size_of::<u8>()); | |
| static_assert!(align_of::<ClassId>() == align_of::<u8>()); | |
| /// Returns the human-readable description of a [`ClassId`]. | |
| #[inline] | |
| pub const fn class_id_to_string(classid: ClassId) -> &'static str { | |
| match classid { | |
| ClassId::GenericVerification => "generic verification", | |
| ClassId::RomTable0x1 => "0x1 ROM table", | |
| ClassId::CoreSight => "CoreSight", | |
| ClassId::PeripheralTestBlock => "peripheral test block", | |
| ClassId::GenericIp => "generic IP", | |
| ClassId::NonStandard => "non-standard", | |
| } | |
| } | |
| layout!({ | |
| /// [CS] B2.2.1 | |
| /// The first component identification register (`CIDR1`). | |
| pub struct ComponentIdRegister(u32); | |
| { | |
| let __ @ 31..8; | |
| /// Should conventionally be called `class` to match the spec, but that is a | |
| /// reserved keyword. | |
| let classid @ 7..4: ClassId; | |
| let prmbl_1 @ 3..0; | |
| } | |
| }); | |
| /// Byte offset of [`ComponentIdRegister`] (`CIDR1`) within a component page. | |
| pub const CIDR1: Offset<ComponentIdRegister, Ro> = Offset::new(0xff4); | |
| layout!({ | |
| /// [CS] B2.2.2 | |
| /// Peripheral ID0 register (`PIDR0`). | |
| pub struct PeripheralId0Register(u32); | |
| { | |
| let __ @ 31..8; | |
| let part0 @ 7..0; | |
| } | |
| }); | |
| /// Byte offset of [`PeripheralId0Register`] (`PIDR0`) within a component page. | |
| pub const PIDR0: Offset<PeripheralId0Register, Ro> = Offset::new(0xfe0); | |
| layout!({ | |
| /// [CS] B2.2.2 | |
| /// Peripheral ID1 register (`PIDR1`). | |
| pub struct PeripheralId1Register(u32); | |
| { | |
| let __ @ 31..8; | |
| let des0 @ 7..4; | |
| let part1 @ 3..0; | |
| } | |
| }); | |
| /// Byte offset of [`PeripheralId1Register`] (`PIDR1`) within a component page. | |
| pub const PIDR1: Offset<PeripheralId1Register, Ro> = Offset::new(0xfe4); | |
| layout!({ | |
| /// [CS] B2.2.2 | |
| /// Peripheral ID2 register (`PIDR2`). | |
| pub struct PeripheralId2Register(u32); | |
| { | |
| let __ @ 31..8; | |
| let revision @ 7..4; | |
| let jedec @ 3; | |
| let des1 @ 2..0; | |
| } | |
| }); | |
| /// Byte offset of [`PeripheralId2Register`] (`PIDR2`) within a component page. | |
| pub const PIDR2: Offset<PeripheralId2Register, Ro> = Offset::new(0xfe8); | |
| layout!({ | |
| /// [CS] B2.2.2 | |
| /// Peripheral ID4 register (`PIDR4`). | |
| pub struct PeripheralId4Register(u32); | |
| { | |
| let __ @ 31..8; | |
| let size @ 7..4; | |
| let des2 @ 3..0; | |
| } | |
| }); | |
| /// Byte offset of [`PeripheralId4Register`] (`PIDR4`) within a component page. | |
| pub const PIDR4: Offset<PeripheralId4Register, Ro> = Offset::new(0xfd0); | |
| /// [CS] B2.2.2 | |
| /// JEDEC ID of the designer. | |
| /// | |
| /// # Safety | |
| /// | |
| /// `bank` must wrap a valid, mapped CoreSight component MMIO region of at least | |
| /// [`MINIMUM_COMPONENT_SIZE`] bytes that is safe to read for the duration of the call. | |
| #[inline] | |
| pub unsafe fn get_designer<Access>(bank: &MmioBank<u32, Access>) -> u16 | |
| where | |
| Access: Accessible + AccessRestrictsTo<Ro>, | |
| { | |
| // SAFETY: Caller guarantees `bank` covers a valid component MMIO region of at least | |
| // `MINIMUM_COMPONENT_SIZE` bytes, which includes `PIDR1`, `PIDR2`, and `PIDR4`. | |
| unsafe { | |
| let des0 = u16::from(bank.at(PIDR1).read().des0()); | |
| let des1 = u16::from(bank.at(PIDR2).read().des1()); | |
| let des2 = u16::from(bank.at(PIDR4).read().des2()); | |
| (des2 << 7) | (des1 << 4) | des0 | |
| } | |
| } | |
| /// [CS] B2.2.2 | |
| /// This number is an ID chosen by the designer. | |
| /// | |
| /// # Safety | |
| /// | |
| /// `bank` must wrap a valid, mapped CoreSight component MMIO region of at least | |
| /// [`MINIMUM_COMPONENT_SIZE`] bytes that is safe to read for the duration of the call. | |
| #[inline] | |
| pub unsafe fn get_part_id<Access>(bank: &MmioBank<u32, Access>) -> u16 | |
| where | |
| Access: Accessible + AccessRestrictsTo<Ro>, | |
| { | |
| // SAFETY: Caller guarantees `bank` covers a valid component MMIO region of at least | |
| // `MINIMUM_COMPONENT_SIZE` bytes, which includes `PIDR0` and `PIDR1`. | |
| unsafe { | |
| let part0 = u16::from(bank.at(PIDR0).read().part0()); | |
| let part1 = u16::from(bank.at(PIDR1).read().part1()); | |
| (part1 << 8) | part0 | |
| } | |
| } | |
| layout!({ | |
| /// [CS] B2.3.3 | |
| /// Used to determine whether two components have an affinity with one another | |
| /// (e.g., if both correspond to the same CPU). | |
| /// | |
| /// This 64-bit register is actually an amalgamation of the two device affinity | |
| /// registers, `DEVAFF0` and `DEVAFF1`. We combine them as, in practice, the | |
| /// resulting value is typically that of the 64-bit `MPIDR` register of the | |
| /// associated CPU. | |
| pub struct DeviceAffinityRegister(u64); | |
| {} | |
| }); | |
| /// Byte offset of [`DeviceAffinityRegister`] (`DEVAFF0`/`DEVAFF1`) within a component page. | |
| pub const DEVAFF: Offset<DeviceAffinityRegister, Ro> = Offset::new(0xfa8); | |
| layout!({ | |
| /// [CS] B2.3.4 | |
| /// Identifies the architect and architecture of a CoreSight component (`DEVARCH`). | |
| pub struct DeviceArchRegister(u32); | |
| { | |
| let architect @ 31..21; | |
| let present @ 20; | |
| let revision @ 19..16; | |
| let archid @ 15..0; | |
| } | |
| }); | |
| /// Byte offset of [`DeviceArchRegister`] (`DEVARCH`) within a component page. | |
| pub const DEVARCH: Offset<DeviceArchRegister, Ro> = Offset::new(0xfbc); | |
| /// Major component type encoded in `DEVTYPE.MAJOR` (`DeviceTypeRegister::MajorType`). | |
| #[bitfield_repr(u8)] | |
| #[derive(Copy, Clone)] | |
| pub enum MajorType { | |
| /// Miscellaneous component (`0x0`). | |
| Miscellaneous = 0x0, | |
| /// Trace sink (`0x1`). | |
| TraceSink = 0x1, | |
| /// Trace link (`0x2`). | |
| TraceLink = 0x2, | |
| /// Trace source (`0x3`). | |
| TraceSource = 0x3, | |
| /// Debug control (`0x4`). | |
| DebugControl = 0x4, | |
| /// Debug logic (`0x5`). | |
| DebugLogic = 0x5, | |
| /// Performance monitor (`0x6`). | |
| PerformanceMonitor = 0x6, | |
| } | |
| static_assert!(size_of::<MajorType>() == size_of::<u8>()); | |
| static_assert!(align_of::<MajorType>() == align_of::<u8>()); | |
| /// Combined CoreSight device type (`DeviceTypeRegister::Type`) decoded from `(major, sub)`. | |
| #[bitfield_repr(u32)] | |
| #[derive(Copy, Clone)] | |
| pub enum DeviceType { | |
| /// Unknown or reserved `(major, sub)` combination. | |
| Unknown = 0, | |
| /// Validation component (`Miscellaneous`, `0x4`). | |
| ValidationComponent, | |
| /// Trace port (`TraceSink`, `0x1`). | |
| TracePort, | |
| /// Trace buffer (`TraceSink`, `0x2`). | |
| TraceBuffer, | |
| /// Trace router (`TraceSink`, `0x3`). | |
| TraceRouter, | |
| /// Trace funnel (`TraceLink`, `0x1`). | |
| TraceFunnel, | |
| /// Trace filter (`TraceLink`, `0x2`). | |
| TraceFilter, | |
| /// Trace FIFO (`TraceLink`, `0x3`). | |
| TraceFifo, | |
| /// CPU trace source (`TraceSource`, `0x1`). | |
| CpuTraceSource, | |
| /// DSP trace source (`TraceSource`, `0x2`). | |
| DspTraceSource, | |
| /// Data engine or coprocessor trace source (`TraceSource`, `0x3`). | |
| DataEngineTraceSource, | |
| /// Bus trace source (`TraceSource`, `0x4`). | |
| BusTraceSource, | |
| /// Software trace source (`TraceSource`, `0x6`). | |
| SoftwareTraceSource, | |
| /// Trigger matrix (`DebugControl`, `0x1`). | |
| TriggerMatrix, | |
| /// Debug authentication module (`DebugControl`, `0x2`). | |
| DebugAuthenticationModule, | |
| /// Power requestor (`DebugControl`, `0x3`). | |
| PowerRequestor, | |
| /// CPU debug logic (`DebugLogic`, `0x1`). | |
| CpuDebugLogic, | |
| /// DSP debug logic (`DebugLogic`, `0x2`). | |
| DspDebugLogic, | |
| /// Data engine or coprocessor debug logic (`DebugLogic`, `0x3`). | |
| DataEngineDebugLogic, | |
| /// Bus debug logic (`DebugLogic`, `0x4`). | |
| BusDebugLogic, | |
| /// Memory debug logic (`DebugLogic`, `0x5`). | |
| MemoryDebugLogic, | |
| /// CPU performance monitor (`PerformanceMonitor`, `0x1`). | |
| CpuPerformanceMonitor, | |
| /// DSP performance monitor (`PerformanceMonitor`, `0x2`). | |
| DspPerformanceMonitor, | |
| /// Data engine or coprocessor performance monitor (`PerformanceMonitor`, `0x3`). | |
| DataEnginePerformanceMonitor, | |
| /// Bus performance monitor (`PerformanceMonitor`, `0x4`). | |
| BusPerformanceMonitor, | |
| /// MMU performance monitor (`PerformanceMonitor`, `0x5`). | |
| MmuPerformanceMonitor, | |
| } | |
| impl DeviceType { | |
| /// Returns the human-readable description of this device type. | |
| #[inline] | |
| pub const fn as_str(self) -> &'static str { | |
| device_type_to_string(self) | |
| } | |
| } | |
| impl fmt::Display for DeviceType { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str(self.as_str()) | |
| } | |
| } | |
| static_assert!(size_of::<DeviceType>() == size_of::<u32>()); | |
| static_assert!(align_of::<DeviceType>() == align_of::<u32>()); | |
| /// Returns the human-readable description of a [`DeviceType`]. | |
| #[inline] | |
| pub const fn device_type_to_string(dev_type: DeviceType) -> &'static str { | |
| match dev_type { | |
| DeviceType::Unknown => "unknown", | |
| DeviceType::ValidationComponent => "validation component", | |
| DeviceType::TracePort => "trace port", | |
| DeviceType::TraceBuffer => "trace buffer", | |
| DeviceType::TraceRouter => "trace router", | |
| DeviceType::TraceFunnel => "trace funnel", | |
| DeviceType::TraceFilter => "trace filter", | |
| DeviceType::TraceFifo => "trace FIFO", | |
| DeviceType::CpuTraceSource => "CPU trace source", | |
| DeviceType::DspTraceSource => "DSP trace source", | |
| DeviceType::DataEngineTraceSource => "data engine or coprocessor trace source", | |
| DeviceType::BusTraceSource => "bus trace source", | |
| DeviceType::SoftwareTraceSource => "software trace source", | |
| DeviceType::TriggerMatrix => "trigger matrix", | |
| DeviceType::DebugAuthenticationModule => "debug authentication module", | |
| DeviceType::PowerRequestor => "power requestor", | |
| DeviceType::CpuDebugLogic => "CPU debug logic", | |
| DeviceType::DspDebugLogic => "DSP debug logic", | |
| DeviceType::DataEngineDebugLogic => "data engine or coprocessor debug logic", | |
| DeviceType::BusDebugLogic => "bus debug logic", | |
| DeviceType::MemoryDebugLogic => "memory debug logic", | |
| DeviceType::CpuPerformanceMonitor => "CPU performance monitor", | |
| DeviceType::DspPerformanceMonitor => "DSP performance monitor", | |
| DeviceType::DataEnginePerformanceMonitor => { | |
| "Data engine or coprocessor performance monitor" | |
| } | |
| DeviceType::BusPerformanceMonitor => "bus performance monitor", | |
| DeviceType::MmuPerformanceMonitor => "MMU performance monitor", | |
| } | |
| } | |
| layout!({ | |
| /// [CS] B2.3.8 | |
| /// Gives high-level information about the type of a CoreSight component (`DEVTYPE`). | |
| pub struct DeviceTypeRegister(u32); | |
| { | |
| let __ @ 31..8; | |
| /// Subtype. | |
| let sub @ 7..4; | |
| let major @ 3..0: MajorType; | |
| } | |
| }); | |
| impl DeviceTypeRegister { | |
| /// The type encoded by `sub` and `major` fields. | |
| #[inline] | |
| pub fn device_type(self) -> DeviceType { | |
| // Allows us to switch across (major, sub-) type pairs. | |
| // [CS] Table B2-9. | |
| match (self.try_major(), self.sub()) { | |
| (Ok(MajorType::Miscellaneous), 0x4) => DeviceType::ValidationComponent, | |
| (Ok(MajorType::TraceSink), 0x1) => DeviceType::TracePort, | |
| (Ok(MajorType::TraceSink), 0x2) => DeviceType::TraceBuffer, | |
| (Ok(MajorType::TraceSink), 0x3) => DeviceType::TraceRouter, | |
| (Ok(MajorType::TraceLink), 0x1) => DeviceType::TraceFunnel, | |
| (Ok(MajorType::TraceLink), 0x2) => DeviceType::TraceFilter, | |
| (Ok(MajorType::TraceLink), 0x3) => DeviceType::TraceFifo, | |
| (Ok(MajorType::TraceSource), 0x1) => DeviceType::CpuTraceSource, | |
| (Ok(MajorType::TraceSource), 0x2) => DeviceType::DspTraceSource, | |
| (Ok(MajorType::TraceSource), 0x3) => DeviceType::DataEngineTraceSource, | |
| (Ok(MajorType::TraceSource), 0x4) => DeviceType::BusTraceSource, | |
| (Ok(MajorType::TraceSource), 0x6) => DeviceType::SoftwareTraceSource, | |
| (Ok(MajorType::DebugControl), 0x1) => DeviceType::TriggerMatrix, | |
| (Ok(MajorType::DebugControl), 0x2) => DeviceType::DebugAuthenticationModule, | |
| (Ok(MajorType::DebugControl), 0x3) => DeviceType::PowerRequestor, | |
| (Ok(MajorType::DebugLogic), 0x1) => DeviceType::CpuDebugLogic, | |
| (Ok(MajorType::DebugLogic), 0x2) => DeviceType::DspDebugLogic, | |
| (Ok(MajorType::DebugLogic), 0x3) => DeviceType::DataEngineDebugLogic, | |
| (Ok(MajorType::DebugLogic), 0x4) => DeviceType::BusDebugLogic, | |
| (Ok(MajorType::DebugLogic), 0x5) => DeviceType::MemoryDebugLogic, | |
| (Ok(MajorType::PerformanceMonitor), 0x1) => DeviceType::CpuPerformanceMonitor, | |
| (Ok(MajorType::PerformanceMonitor), 0x2) => DeviceType::DspPerformanceMonitor, | |
| (Ok(MajorType::PerformanceMonitor), 0x3) => DeviceType::DataEnginePerformanceMonitor, | |
| (Ok(MajorType::PerformanceMonitor), 0x4) => DeviceType::BusPerformanceMonitor, | |
| (Ok(MajorType::PerformanceMonitor), 0x5) => DeviceType::MmuPerformanceMonitor, | |
| _ => DeviceType::Unknown, | |
| } | |
| } | |
| } | |
| /// Byte offset of [`DeviceTypeRegister`] (`DEVTYPE`) within a component page. | |
| pub const DEVTYPE: Offset<DeviceTypeRegister, Ro> = Offset::new(0xfcc); | |
| /// FFI trampoline for `coresight::DeviceTypeRegister::type()`. | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn rust_coresight_device_type_from_raw(reg_value: u32) -> u32 { | |
| DeviceTypeRegister::from(reg_value).device_type() as u32 | |
| } | |
| /// FFI trampoline for `coresight::ToString(ComponentIdRegister::Class)`. | |
| /// | |
| /// # Safety | |
| /// | |
| /// `out_len` must be a valid, aligned pointer writable for the duration of the call. | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn rust_coresight_class_id_to_string( | |
| raw_classid: u8, | |
| out_len: *mut usize, | |
| ) -> *const c_char { | |
| let s = | |
| zerocopy::TryFromBytes::try_read_from_bytes(zerocopy::IntoBytes::as_bytes(&raw_classid)) | |
| .map_or("unknown", class_id_to_string); | |
| // SAFETY: Caller guarantees `out_len` is a valid, aligned, writable pointer. | |
| unsafe { | |
| out_len.write(s.len()); | |
| } | |
| s.as_ptr().cast::<c_char>() | |
| } | |
| /// FFI trampoline for `coresight::ToString(DeviceTypeRegister::Type)`. | |
| /// | |
| /// # Safety | |
| /// | |
| /// `out_len` must be a valid, aligned pointer writable for the duration of the call. | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn rust_coresight_device_type_to_string( | |
| raw_dev_type: u32, | |
| out_len: *mut usize, | |
| ) -> *const c_char { | |
| let dev_type = | |
| zerocopy::TryFromBytes::try_read_from_bytes(zerocopy::IntoBytes::as_bytes(&raw_dev_type)) | |
| .unwrap_or(DeviceType::Unknown); | |
| let s = device_type_to_string(dev_type); | |
| // SAFETY: Caller guarantees `out_len` is a valid, aligned, writable pointer. | |
| unsafe { | |
| out_len.write(s.len()); | |
| } | |
| s.as_ptr().cast::<c_char>() | |
| } | |
| /// FFI trampoline for `coresight::GetDesigner`. | |
| /// | |
| /// # Safety | |
| /// | |
| /// `base` must be a valid, 4-byte aligned pointer to a mapped CoreSight component MMIO region of | |
| /// at least [`MINIMUM_COMPONENT_SIZE`] bytes that is safe to read for the duration of the call. | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn rust_coresight_get_designer(base: *const u32) -> u16 { | |
| // SAFETY: Caller guarantees `base` is a valid, aligned pointer to a mapped component MMIO | |
| // region of at least `MINIMUM_COMPONENT_SIZE` bytes. | |
| unsafe { | |
| let ptr = MmioPtr::<u32, Ro>::new(base); | |
| let bank = MmioBank::new(ptr, MINIMUM_COMPONENT_SIZE); | |
| get_designer(&bank) | |
| } | |
| } | |
| /// FFI trampoline for `coresight::GetPartId`. | |
| /// | |
| /// # Safety | |
| /// | |
| /// `base` must be a valid, 4-byte aligned pointer to a mapped CoreSight component MMIO region of | |
| /// at least [`MINIMUM_COMPONENT_SIZE`] bytes that is safe to read for the duration of the call. | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn rust_coresight_get_part_id(base: *const u32) -> u16 { | |
| // SAFETY: Caller guarantees `base` is a valid, aligned pointer to a mapped component MMIO | |
| // region of at least `MINIMUM_COMPONENT_SIZE` bytes. | |
| unsafe { | |
| let ptr = MmioPtr::<u32, Ro>::new(base); | |
| let bank = MmioBank::new(ptr, MINIMUM_COMPONENT_SIZE); | |
| get_part_id(&bank) | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[repr(C, align(8))] | |
| struct FakeComponentPage { | |
| words: [u32; MINIMUM_COMPONENT_SIZE / size_of::<u32>()], | |
| } | |
| impl FakeComponentPage { | |
| const fn new() -> Self { | |
| Self { words: [0; MINIMUM_COMPONENT_SIZE / size_of::<u32>()] } | |
| } | |
| fn write32(&mut self, offset: usize, val: u32) { | |
| self.words[offset / size_of::<u32>()] = val; | |
| } | |
| fn write64(&mut self, offset: usize, val: u64) { | |
| let idx = offset / size_of::<u32>(); | |
| let bytes = val.to_ne_bytes(); | |
| self.words[idx] = u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); | |
| self.words[idx + 1] = u32::from_ne_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]); | |
| } | |
| fn bank(&self) -> MmioBank<u32, Ro> { | |
| // SAFETY: `self.words` is a valid, 8-byte aligned 4 KiB region alive for the borrow. | |
| let ptr = unsafe { MmioPtr::<u32, Ro>::new(self.words.as_ptr()) }; | |
| MmioBank::new(ptr, MINIMUM_COMPONENT_SIZE) | |
| } | |
| fn bank64(&self) -> MmioBank<u64, Ro> { | |
| // SAFETY: `self.words` is a valid, 8-byte aligned 4 KiB region alive for the borrow. | |
| let ptr = unsafe { MmioPtr::<u64, Ro>::new(self.words.as_ptr().cast::<u64>()) }; | |
| MmioBank::new(ptr, MINIMUM_COMPONENT_SIZE) | |
| } | |
| fn as_ptr(&self) -> *const u32 { | |
| self.words.as_ptr() | |
| } | |
| } | |
| #[test] | |
| fn device_type_decoding_and_strings() { | |
| let cases = [ | |
| ( | |
| (MajorType::Miscellaneous, 0x4), | |
| DeviceType::ValidationComponent, | |
| "validation component", | |
| ), | |
| ((MajorType::TraceSink, 0x1), DeviceType::TracePort, "trace port"), | |
| ((MajorType::TraceSink, 0x2), DeviceType::TraceBuffer, "trace buffer"), | |
| ((MajorType::TraceSink, 0x3), DeviceType::TraceRouter, "trace router"), | |
| ((MajorType::TraceLink, 0x1), DeviceType::TraceFunnel, "trace funnel"), | |
| ((MajorType::TraceLink, 0x2), DeviceType::TraceFilter, "trace filter"), | |
| ((MajorType::TraceLink, 0x3), DeviceType::TraceFifo, "trace FIFO"), | |
| ((MajorType::TraceSource, 0x1), DeviceType::CpuTraceSource, "CPU trace source"), | |
| ((MajorType::TraceSource, 0x2), DeviceType::DspTraceSource, "DSP trace source"), | |
| ( | |
| (MajorType::TraceSource, 0x3), | |
| DeviceType::DataEngineTraceSource, | |
| "data engine or coprocessor trace source", | |
| ), | |
| ((MajorType::TraceSource, 0x4), DeviceType::BusTraceSource, "bus trace source"), | |
| ( | |
| (MajorType::TraceSource, 0x6), | |
| DeviceType::SoftwareTraceSource, | |
| "software trace source", | |
| ), | |
| ((MajorType::DebugControl, 0x1), DeviceType::TriggerMatrix, "trigger matrix"), | |
| ( | |
| (MajorType::DebugControl, 0x2), | |
| DeviceType::DebugAuthenticationModule, | |
| "debug authentication module", | |
| ), | |
| ((MajorType::DebugControl, 0x3), DeviceType::PowerRequestor, "power requestor"), | |
| ((MajorType::DebugLogic, 0x1), DeviceType::CpuDebugLogic, "CPU debug logic"), | |
| ((MajorType::DebugLogic, 0x2), DeviceType::DspDebugLogic, "DSP debug logic"), | |
| ( | |
| (MajorType::DebugLogic, 0x3), | |
| DeviceType::DataEngineDebugLogic, | |
| "data engine or coprocessor debug logic", | |
| ), | |
| ((MajorType::DebugLogic, 0x4), DeviceType::BusDebugLogic, "bus debug logic"), | |
| ((MajorType::DebugLogic, 0x5), DeviceType::MemoryDebugLogic, "memory debug logic"), | |
| ( | |
| (MajorType::PerformanceMonitor, 0x1), | |
| DeviceType::CpuPerformanceMonitor, | |
| "CPU performance monitor", | |
| ), | |
| ( | |
| (MajorType::PerformanceMonitor, 0x2), | |
| DeviceType::DspPerformanceMonitor, | |
| "DSP performance monitor", | |
| ), | |
| ( | |
| (MajorType::PerformanceMonitor, 0x3), | |
| DeviceType::DataEnginePerformanceMonitor, | |
| "Data engine or coprocessor performance monitor", | |
| ), | |
| ( | |
| (MajorType::PerformanceMonitor, 0x4), | |
| DeviceType::BusPerformanceMonitor, | |
| "bus performance monitor", | |
| ), | |
| ( | |
| (MajorType::PerformanceMonitor, 0x5), | |
| DeviceType::MmuPerformanceMonitor, | |
| "MMU performance monitor", | |
| ), | |
| ]; | |
| for ((major, sub), expected_type, expected_str) in cases { | |
| let mut reg = DeviceTypeRegister::default(); | |
| reg.set_major(major).set_sub(sub); | |
| assert_eq!(reg.device_type(), expected_type); | |
| assert_eq!(reg.device_type().as_str(), expected_str); | |
| assert_eq!(rust_coresight_device_type_from_raw(reg.bits()), expected_type as u32); | |
| } | |
| let unknown = DeviceTypeRegister::from(0xff); | |
| assert_eq!(unknown.device_type(), DeviceType::Unknown); | |
| assert_eq!(unknown.device_type().as_str(), "unknown"); | |
| } | |
| #[test] | |
| fn class_id_decoding_and_strings() { | |
| assert_eq!(class_id_to_string(ClassId::GenericVerification), "generic verification"); | |
| assert_eq!(class_id_to_string(ClassId::RomTable0x1), "0x1 ROM table"); | |
| assert_eq!(class_id_to_string(ClassId::CoreSight), "CoreSight"); | |
| assert_eq!(class_id_to_string(ClassId::PeripheralTestBlock), "peripheral test block"); | |
| assert_eq!(class_id_to_string(ClassId::GenericIp), "generic IP"); | |
| assert_eq!(class_id_to_string(ClassId::NonStandard), "non-standard"); | |
| } | |
| #[test] | |
| fn designer_and_part_id() { | |
| let mut pidr0 = PeripheralId0Register::default(); | |
| pidr0.set_part0(0x06); | |
| let mut pidr1 = PeripheralId1Register::default(); | |
| pidr1.set_part1(0x09).set_des0(0xb); | |
| let mut pidr2 = PeripheralId2Register::default(); | |
| pidr2.set_des1(0x3).set_jedec(true).set_revision(0x2); | |
| let mut pidr4 = PeripheralId4Register::default(); | |
| pidr4.set_des2(0x4).set_size(0x0); | |
| let mut page = FakeComponentPage::new(); | |
| page.write32(PIDR0.value, pidr0.bits()); | |
| page.write32(PIDR1.value, pidr1.bits()); | |
| page.write32(PIDR2.value, pidr2.bits()); | |
| page.write32(PIDR4.value, pidr4.bits()); | |
| page.write64(DEVAFF.value, 0x8000_0001); | |
| page.write32(CIDR1.value, 0x90); | |
| let bank = page.bank(); | |
| let bank64 = page.bank64(); | |
| // SAFETY: `page` is a valid, 8-byte aligned 4 KiB component buffer. | |
| unsafe { | |
| assert_eq!(get_designer(&bank), arm::ARCHITECT); | |
| assert_eq!(get_part_id(&bank), arm::partid::CTI_400); | |
| assert_eq!(rust_coresight_get_designer(page.as_ptr()), arm::ARCHITECT); | |
| assert_eq!(rust_coresight_get_part_id(page.as_ptr()), arm::partid::CTI_400); | |
| assert_eq!(bank64.at(DEVAFF).read().bits(), 0x8000_0001); | |
| assert_eq!(bank.at(CIDR1).read().try_classid().ok(), Some(ClassId::CoreSight)); | |
| } | |
| } | |
| #[test] | |
| fn ffi_trampolines() { | |
| let mut len = 0usize; | |
| // SAFETY: `&mut len` is a valid, aligned pointer, and the FFI functions return valid | |
| // static UTF-8 slices of length `len`. | |
| unsafe { | |
| let ptr = rust_coresight_class_id_to_string(ClassId::CoreSight as u8, &mut len); | |
| let s = | |
| core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr.cast::<u8>(), len)); | |
| assert_eq!(s, "CoreSight"); | |
| let ptr = rust_coresight_class_id_to_string(0x5, &mut len); | |
| let s = | |
| core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr.cast::<u8>(), len)); | |
| assert_eq!(s, "unknown"); | |
| let ptr = | |
| rust_coresight_device_type_to_string(DeviceType::CpuDebugLogic as u32, &mut len); | |
| let s = | |
| core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr.cast::<u8>(), len)); | |
| assert_eq!(s, "CPU debug logic"); | |
| let ptr = rust_coresight_device_type_to_string(0xffff, &mut len); | |
| let s = | |
| core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr.cast::<u8>(), len)); | |
| assert_eq!(s, "unknown"); | |
| } | |
| } | |
| } |