1/* 2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6 7#pragma once 8 9#include <config.h> 10 11#if defined(CONFIG_HARDWARE_DEBUG_API) || defined(CONFIG_EXPORT_PMU_USER) || defined(CONFIG_ENABLE_BENCHMARKS) 12 13#include <mode/machine/debug.h> 14#include <mode/machine.h> /* MRC/MCR */ 15 16/** Read DBGDSCR from CP14. 17 */ 18static inline word_t readDscrCp(void) 19{ 20 word_t v; 21#ifdef CONFIG_ARM_CORTEX_A8 22 MRC(DBGDSCR_int, v); 23#else 24 MRC(DBGDSCR_ext, v); 25#endif 26 return v; 27} 28 29/** Write DBGDSCR (Status and control register). 30 * On ARMv7, the external view of the CP14 DBGDSCR register is preferred since 31 * the internal view is fully read-only. 32 */ 33static inline void writeDscrCp(word_t val) 34{ 35 MCR(DBGDSCR_ext, val); 36} 37#endif /* CONFIG_HARDWARE_DEBUG_API CONFIG_EXPORT_PMU_USER */ 38 39#ifdef CONFIG_HARDWARE_DEBUG_API 40#define DBGVCR_RESERVED_BITS_MASK \ 41 (BIT(5)|BIT(8)|BIT(9)|BIT(13)|BIT(16)|BIT(24)|BIT(29)) 42 43#define DBGWCR_BAS_HIGH_SHIFT (9u) 44#define DBGWCR_0 "p14,0,%0,c0,c0,7" 45 46enum v7_breakpoint_type { 47 DBGBCR_TYPE_UNLINKED_INSTRUCTION_MATCH = 0u, 48 DBGBCR_TYPE_LINKED_INSTRUCTION_MATCH = 0x1u, 49 DBGBCR_TYPE_UNLINKED_CONTEXT_MATCH = 0x2u, 50 DBGBCR_TYPE_LINKED_CONTEXT_MATCH = 0x3u, 51 52 DBGBCR_TYPE_UNLINKED_INSTRUCTION_MISMATCH = 0x4u, 53 DBGBCR_TYPE_LINKED_INSTRUCTION_MISMATCH = 0x5u, 54 55 DBGBCR_TYPE_UNLINKED_VMID_MATCH = 0x8u, 56 DBGBCR_TYPE_LINKED_VMID_MATCH = 0x9u, 57 DBGBCR_TYPE_UNLINKED_VMID_AND_CONTEXT_MATCH = 0xAu, 58 DBGBCR_TYPE_LINKED_VMID_AND_CONTEXT_MATCH = 0xBu 59}; 60 61 62/** Determines whether or not 8-byte watchpoints are supported. 63 * 64 * Checks to see if the 8-byte byte-address-select high bits ignore writes. 65 */ 66static inline bool_t watchpoint8bSupported(void) 67{ 68 word_t wcrtmp; 69 70 /* ARMv7 manual: C11.11.44: 71 * "A 4-bit Byte address select field is DBGWCR[8:5]. DBGWCR[12:9] is RAZ/WI." 72 * 73 * So if 8-byte WPs aren't supported, then the higher 4-bits of the BAS 74 * field will be RAZ/WI. We can just test the first WP's BAS bits and see 75 * what happens. 76 */ 77 MRC(DBGWCR_0, wcrtmp); 78 wcrtmp |= BIT(DBGWCR_BAS_HIGH_SHIFT); 79 MCR(DBGWCR_0, wcrtmp); 80 81 /* Re-read to know if the write to the bit was ignored */ 82 MRC(DBGWCR_0, wcrtmp); 83 return wcrtmp & BIT(DBGWCR_BAS_HIGH_SHIFT); 84} 85 86/** Enables the debug architecture mode that allows us to receive debug events 87 * as exceptions. 88 * 89 * CPU can operate in one of 2 debug architecture modes: "halting" and 90 * "monitor". In halting mode, when a debug event occurs, the CPU will halt 91 * execution and enter a special state in which it can be examined by an 92 * external debugger dongle. 93 * 94 * In monitor mode, the CPU will deliver debug events to the kernel as 95 * exceptions. Monitor mode is what's actually useful to us. If it's not 96 * supported by the CPU, it's impossible for the API to work. 97 * 98 * Unfortunately, it's also gated behind a hardware pin signal, #DBGEN. If 99 * #DBGEN is held low, monitor mode is unavailable. 100 */ 101BOOT_CODE static bool_t enableMonitorMode(void) 102{ 103 dbg_dscr_t dscr; 104 105 dscr.words[0] = readDscrCp(); 106 dscr = dbg_dscr_set_haltingDebugEnable(dscr, 0); 107 dscr = dbg_dscr_set_disableAllUserAccesses(dscr, 1); 108 dscr = dbg_dscr_set_monitorDebugEnable(dscr, 1); 109 110 writeDscrCp(dscr.words[0]); 111 isb(); 112 113 /* We can tell if the #DBGEN signal is enabled by setting 114 * the DBGDSCR.MDBGEn bit. If the #DBGEN signal is not enabled, writes 115 * to DBGDSCR.MDBGEn will be ignored, and it will always read as zero. 116 * 117 * We test here to see if the DBGDSCR.MDBGEn bit is still 0, even after 118 * we set it to 1 in enableMonitorMode(). 119 * 120 * ARMv6 manual, sec D3.3.2, "Monitor debug-mode enable, bit[15]": 121 * 122 * "Monitor debug-mode has to be both selected and enabled (bit 14 123 * clear and bit 15 set) for the core to take a Debug exception." 124 * 125 * "If the external interface input DBGEN is low, DSCR[15:14] reads as 126 * 0b00. The programmed value is masked until DBGEN is taken high, at 127 * which time value is read and behavior reverts to the programmed 128 * value." 129 */ 130 /* Re-read the value */ 131 dscr.words[0] = readDscrCp(); 132 if (dbg_dscr_get_monitorDebugEnable(dscr) == 0) { 133 printf("#DBGEN signal held low. Monitor mode unavailable.\n"); 134 return false; 135 } 136 return true; 137} 138 139static inline dbg_bcr_t Arch_setupBcr(dbg_bcr_t in_val, bool_t is_match) 140{ 141 dbg_bcr_t bcr; 142 143 bcr = dbg_bcr_set_addressMask(in_val, 0); 144 bcr = dbg_bcr_set_hypeModeControl(bcr, 0); 145 bcr = dbg_bcr_set_secureStateControl(bcr, 0); 146 if (is_match) { 147 bcr = dbg_bcr_set_breakpointType(bcr, DBGBCR_TYPE_UNLINKED_INSTRUCTION_MATCH); 148 } else { 149 bcr = dbg_bcr_set_breakpointType(bcr, DBGBCR_TYPE_UNLINKED_INSTRUCTION_MISMATCH); 150 } 151 return bcr; 152} 153 154static inline dbg_wcr_t Arch_setupWcr(dbg_wcr_t in_val) 155{ 156 dbg_wcr_t wcr; 157 158 wcr = dbg_wcr_set_addressMask(in_val, 0); 159 wcr = dbg_wcr_set_hypeModeControl(wcr, 0); 160 wcr = dbg_wcr_set_secureStateControl(wcr, 0); 161 return wcr; 162} 163 164static inline bool_t Arch_breakpointIsMismatch(dbg_bcr_t in_val) 165{ 166 /* Detect if the register is set up for mismatch (single-step). */ 167 if (dbg_bcr_get_breakpointType(in_val) == DBGBCR_TYPE_UNLINKED_INSTRUCTION_MISMATCH) { 168 return true; 169 } 170 return false; 171} 172 173#endif /* CONFIG_HARDWARE_DEBUG_API */ 174 175