#![allow(non_upper_case_globals)]
use aarch32_cpu::register::{SysReg, SysRegRead, SysRegWrite};
use arbitrary_int::*;
use bitbybit::{bitenum, bitfield};
#[must_use]
pub const fn get_cp14_encoding(register: DebugRegister) -> (u32, u32, u32) {
let regnum = register.to_raw();
(
(regnum >> 7) & 0b111,
regnum & 0b1111,
(regnum >> 4) & 0b111,
)
}
#[macro_export]
macro_rules! read_dbgreg {
($reg:expr) => {
unsafe {
const ENCODING: (u32, u32, u32) = $crate::cpu::debug::get_cp14_encoding($reg);
let value: u32;
core::arch::asm!(
"mrc p14, 0, {value}, c{CRn}, c{CRm}, {opc2}",
value = out(reg) value,
CRn = const ENCODING.0,
CRm = const ENCODING.1,
opc2 = const ENCODING.2,
options(nostack, preserves_flags),
);
value
}
};
}
#[macro_export]
macro_rules! write_dbgreg {
($reg:expr, $value:expr) => {
{
const ENCODING: (u32, u32, u32) = $crate::cpu::debug::get_cp14_encoding($reg);
let value: u32 = $value;
core::arch::asm!(
"mcr p14, 0, {value}, c{CRn}, c{CRm}, {opc2}",
value = in(reg) value,
CRn = const ENCODING.0,
CRm = const ENCODING.1,
opc2 = const ENCODING.2,
options(nostack, preserves_flags),
);
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DebugRegister(u32);
impl DebugRegister {
pub const fn to_raw(self) -> u32 {
self.0
}
}
pub const DBGDIDR: DebugRegister = DebugRegister(0);
pub const DBGDSCRint: DebugRegister = DebugRegister(1);
pub const DBGDSCRext: DebugRegister = DebugRegister(34);
pub const DBGDRAR: DebugRegister = DebugRegister(128);
pub const DBGDSAR: DebugRegister = DebugRegister(256);
#[bitfield(u32, debug)]
pub struct DebugID {
#[bits(28..=31, r)]
wrps: u4,
#[bits(24..=27, r)]
brps: u4,
#[bits(16..=19, r)]
version: Option<DebugVersion>,
}
impl DebugID {
pub fn read() -> Self {
Self::new_with_raw_value(read_dbgreg!(DBGDIDR))
}
}
#[derive(Debug, PartialEq, Eq)]
#[bitenum(u4, exhaustive = false)]
pub enum DebugVersion {
V6 = 0b0001,
V6_1 = 0b0010,
V7Full = 0b0011,
V7Minimal = 0b0100,
V7_1 = 0b0101,
}
#[bitfield(u32, debug)]
pub struct DebugStatusControl {
#[bit(16, r)]
secure_pl1_invasive_debug_disabled: bool,
#[bit(15, rw)]
monitor_debug_mode: bool,
#[bit(14, rw)]
halting_debug_mode: bool,
#[bits(2..=5, r)]
method_of_entry: Option<DebugEventReason>,
}
#[bitenum(u4, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum DebugEventReason {
HaltReq = 0b0000,
Breakpoint = 0b0001,
AsyncWatchpoint = 0b0010,
BkptInstr = 0b0011,
ExtDebugReq = 0b0100,
VectorCatch = 0b0101,
OSUnlock = 0b1000,
SyncWatchpoint = 0b1010,
}
#[derive(Debug, PartialEq, Eq)]
#[bitenum(u2, exhaustive = false)]
pub enum DebugValid {
Invalid = 0b00,
Valid = 0b11,
}
#[bitfield(u32, debug)]
pub struct DebugROMAddress {
#[bits(12..=31, r)]
romaddr: u20,
#[bits(0..=1, r)]
valid: Option<DebugValid>,
}
impl DebugROMAddress {
pub fn read() -> Self {
Self::new_with_raw_value(read_dbgreg!(DBGDRAR))
}
#[must_use]
pub const fn value(self) -> usize {
(self.romaddr().value() << 12) as usize
}
}
#[bitfield(u32, debug)]
pub struct DebugSelfAddressOffset {
#[bits(12..=31, r)]
selfoffset: u20,
#[bits(0..=1, r)]
valid: Option<DebugValid>,
}
impl DebugSelfAddressOffset {
pub fn read() -> Self {
Self::new_with_raw_value(read_dbgreg!(DBGDSAR))
}
#[must_use]
pub const fn value(self) -> isize {
(self.selfoffset().value() << 12) as isize
}
}
#[bitfield(u32, debug)]
pub struct SecureDebugEnable {
#[bit(0, rw)]
secure_user_invasive_debug: bool,
#[bit(1, rw)]
secure_user_noninvasive_debug: bool,
}
impl SysReg for SecureDebugEnable {
const CP: u32 = 15;
const CRN: u32 = 1;
const OP1: u32 = 0;
const CRM: u32 = 1;
const OP2: u32 = 1;
}
impl SysRegRead for SecureDebugEnable {}
impl SysRegWrite for SecureDebugEnable {}
impl SecureDebugEnable {
pub fn read() -> Self {
Self::new_with_raw_value(Self::read_raw())
}
pub fn write(self) {
unsafe {
Self::write_raw(self.raw_value());
}
}
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct DebugLogic {
#[mmio(PureRead)]
id: DebugID,
_status_control_int: DebugStatusControl,
_reserved0: [u32; 3],
_data_transfer_int: u32,
_watchpoint_fault_addr: u32,
vector_catch: u32,
_reserved1: u32,
event_catch: u32,
debug_cache_ctrl: u32,
debug_mmu_ctrl: u32,
_reserved2: [u32; 20],
host_to_target_data_transfer_ext: u32,
#[mmio(Write)]
instr_tx: u32,
status_control_ext: DebugStatusControl,
target_to_host_data_transfer_ext: u32,
#[mmio(Write)]
run_ctrl: u32,
ext_auxiliary_ctrl: u32,
_reserved3: [u32; 2],
#[mmio(PureRead)]
pc_sample: u32,
#[mmio(PureRead)]
context_id_sample: u32,
_virt_id_sample: u32,
_reserved4: [u32; 21],
breakpoint_value: [u32; 16],
breakpoint_ctrl: [BreakpointControl; 16],
watchpoint_value: [u32; 16],
watchpoint_ctrl: [WatchpointControl; 16],
_debug_rom_addr: u32,
_reserved5: [u32; 15],
_breakpoint_extd_value: [u32; 16],
_reserved6: [u32; 32],
_os_lock_access: u32,
#[mmio(PureRead)]
os_lock_status: u32,
_os_save_restore: u32,
_os_double_lock: u32,
powerdown_reset_ctrl: u32,
#[mmio(PureRead)]
powerdown_reset_status: u32,
_reserved7: [u32; 58],
_debug_self_addr_offset: u32,
_reserved8: [u32; 255],
_impl_defined0: [u32; 64],
_reserved9: [u32; 256],
#[mmio(PureRead)]
processor_id: [u32; 64],
_reserved10: [u32; 32],
_integration: [u32; 32],
_integration_mode_ctrl: u32,
_reserved11: [u32; 39],
claim_tag_set: u32,
claim_tag_clear: u32,
_reserved12: [u32; 2],
#[mmio(Write)]
lock_access: u32,
#[mmio(PureRead)]
lock_status: SoftwareLock,
#[mmio(PureRead)]
authentication_status: u32,
_reserved13: u32,
_debug_device_id_2: u32,
_debug_device_id_1: u32,
#[mmio(PureRead)]
device_type: u32,
#[mmio(PureRead)]
peripheral_id_4: u32,
#[mmio(PureRead)]
peripheral_ids: [u32; 4],
#[mmio(PureRead)]
component_ids: [u32; 4],
}
pub const DEBUG_UNLOCK_MAGIC: u32 = 0xC5ACCE55;
#[bitfield(u32, debug)]
pub struct BreakpointControl {
#[bits(24..=28, rw)]
address_range_mask: u5,
#[bits(20..=23, rw)]
breakpoint_type: Option<BreakpointType>,
#[bits(16..=19, rw)]
linked_breakpoint_index: u4,
#[bits(14..=15, rw)]
security_state_ctrl: Option<SecurityFilter>,
#[bits(5..=8, rw)]
byte_address_select: u4,
#[bits(1..=2, rw)]
privileged_mode_ctrl: PrivilegeModeFilter,
#[bit(0, rw)]
enabled: bool,
}
impl BreakpointControl {
pub const DISABLED: Self = BreakpointControl::new_with_raw_value(0).with_enabled(false);
}
#[bitenum(u4, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum BreakpointType {
UnlinkedInstrAddressMatch = 0b0000,
LinkedInstrAddressMatch = 0b0001,
UnlinkedContextIDMatch = 0b0010,
LinkedContextIDMatch = 0b0011,
UnlinkedInstrAddressMismatch = 0b0100,
LinkedInstrAddressMismatch = 0b0101,
}
#[bitenum(u2, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum SecurityFilter {
All = 0b00,
NotSecureOnly = 0b01,
SecureOnly = 0b10,
}
#[bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum PrivilegeModeFilter {
UserSystemSupervisorOnly = 0b00,
Level1Only = 0b01,
UserOnly = 0b10,
All = 0b11,
}
#[bitfield(u32, debug)]
pub struct WatchpointControl {
#[bit(20, rw)]
watchpoint_type: WatchpointType,
#[bits(16..=19, rw)]
linked_breakpoint_index: u4,
#[bits(14..=15, rw)]
security_state_ctrl: Option<SecurityFilter>,
#[bits(5..=8, rw)]
byte_address_select: u4,
#[bits(3..=4, rw)]
load_store_ctrl: Option<LoadStoreFilter>,
#[bits(1..=2, rw)]
privileged_access_ctrl: Option<PrivilegedAccessFilter>,
#[bit(0, rw)]
enabled: bool,
}
impl WatchpointControl {
pub const DISABLED: Self = WatchpointControl::new_with_raw_value(0).with_enabled(false);
}
#[bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum WatchpointType {
UnlinkedDataAddressMatch = 0,
LinkedDataAddressMatch = 1,
}
#[bitenum(u2, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum LoadStoreFilter {
LoadSwapOnly = 0b01,
StoreSwapOnly = 0b10,
All = 0b11,
}
#[bitenum(u2, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum PrivilegedAccessFilter {
Level1Only = 0b01,
UserOnly = 0b10,
All = 0b11,
}
#[bitfield(u32)]
pub struct SoftwareLock {
#[bit(2, r)]
not_32bit_access: bool,
#[bit(1, r)]
software_lock_status: bool,
#[bit(0, r)]
software_lock_implemented: bool,
}