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 <util.h> 10#include <api/types.h> 11#include <arch/machine/debug_conf.h> 12#include <sel4/plat/api/constants.h> 13#include <armv/debug.h> 14 15#ifdef ARM_BASE_CP14_SAVE_AND_RESTORE 16void restore_user_debug_context(tcb_t *target_thread); 17void saveAllBreakpointState(tcb_t *t); 18void loadAllDisabledBreakpointState(void); 19#endif 20#ifdef ARM_HYP_CP14_SAVE_AND_RESTORE_VCPU_THREADS 21void Arch_debugAssociateVCPUTCB(tcb_t *t); 22void Arch_debugDissociateVCPUTCB(tcb_t *t); 23#endif 24 25#ifdef ARM_HYP_TRAP_CP14 26/* Those of these that trap NS accesses trap all NS accesses; we can't cause the 27 * processor to only trap NS-PL0 or NS-PL1, but if we want to trap the accesses, 28 * we get both (PL0 and PL1) non-secure modes' accesses. 29 */ 30#define ARM_CP15_HDCR "p15, 4, %0, c1, c1, 1" 31#define HDCR_DEBUG_TDRA_SHIFT (11) /* Trap debug ROM access from non-secure world */ 32#define HDCR_DEBUG_TDOSA_SHIFT (10) /* Trap debug OS related access from NS world */ 33#define HDCR_DEBUG_TDA_SHIFT (9) /* Trap debug CP14 register access from NS world */ 34#define HDCR_DEBUG_TDE_SHIFT (8) /* Trap debug exceptions taken from NS world */ 35#define HDCR_PERFMON_HPME_SHIFT (7) /* Enable the hyp-mode perfmon counters. */ 36#define HDCR_PERFMON_TPM_SHIFT (6) /* Trap NS PM accesses */ 37#define HDCR_PERFMON_TPMCR_SHIFT (5) /* Trap NS PMCR reg access */ 38 39/** When running seL4 as a hypervisor, if we're building with support for the 40 * hardware debug API, we have a case of indirection that we need to handle. 41 * 42 * For native PL0 user threads in the hypervisor seL4 build, if a debug 43 * exception is triggered in one of them, the CPU will raise the exception and 44 * naturally, it will attempt to deliver it to a PL1 exception vector table -- 45 * but no such table exists for native hypervisor-seL4 threads, so the CPU will 46 * end up encountering a VM fault while trying to vector into the vector table. 47 * 48 * For this reason, for native hypervisor-seL4 threads, we need to trap the 49 * debug exception DIRECTLY into the hypervisor-seL4 instance, and handle it 50 * directly. So we need to SET HDCR.TDE for this case. 51 * 52 * For the Guest VM, if it programs the CPU to trigger breakpoints, and a 53 * debug exception gets triggered, we don't want to catch those debug exceptions 54 * since we can let the Guest VM handle them on its own. So we need to UNSET 55 * HDCR.TDE for this case. 56 * 57 * This function encapsulates the setting/unsetting, and it is called when we 58 * are about to enable/disable a VCPU. 59 * 60 * If we are enabling a vcpu (vcpu_enable) we UNSET HDCR.TDE. 61 * If we are disabling a vcpu (vcpu_disable) we SET HDCR.TDE. 62 */ 63static inline void setHDCRTrapDebugExceptionState(bool_t enable_trapping) 64{ 65 word_t hdcr; 66#ifdef CONFIG_ARCH_AARCH64 67 MRS("mdcr_el2", hdcr); 68#else 69 MRC(ARM_CP15_HDCR, hdcr); 70#endif 71 if (enable_trapping) { 72 /* Trap and redirect debug faults that occur in PL0 native threads by 73 * setting HDCR.TDE (trap debug exceptions). 74 */ 75 hdcr |= (BIT(HDCR_DEBUG_TDE_SHIFT) 76 | BIT(HDCR_DEBUG_TDA_SHIFT) 77 | BIT(HDCR_DEBUG_TDRA_SHIFT) 78 | BIT(HDCR_DEBUG_TDOSA_SHIFT)); 79 } else { 80 /* Let the PL1 Guest VM handle debug events on its own */ 81 hdcr &= ~(BIT(HDCR_DEBUG_TDE_SHIFT) 82 | BIT(HDCR_DEBUG_TDA_SHIFT) 83 | BIT(HDCR_DEBUG_TDRA_SHIFT) 84 | BIT(HDCR_DEBUG_TDOSA_SHIFT)); 85 } 86#ifdef CONFIG_ARCH_AARCH64 87 MSR("mdcr_el2", hdcr); 88#else 89 MCR(ARM_CP15_HDCR, hdcr); 90#endif 91} 92 93static inline void initHDCR(void) 94{ 95 /* By default at boot, we SET HDCR.TDE to catch and redirect native threads' 96 * PL0 debug exceptions. 97 * 98 * Unfortunately, this is complicated a bit by ARM's strange requirement that 99 * if you set HDCR.TDE, you must also set TDA, TDOSA, and TDRA: 100 * ARMv7 archref manual: section B1.8.9: 101 * "When HDCR.TDE is set to 1, the HDCR.{TDRA, TDOSA, TDA} bits must all 102 * be set to 1, otherwise behavior is UNPREDICTABLE" 103 * 104 * Subsequently on calls to vcpu_enable/disable, we will modify HDCR.TDE 105 * as needed. 106 */ 107 setHDCRTrapDebugExceptionState(true); 108} 109#endif /* ARM_HYP_TRAP_CP14 */ 110 111#ifdef CONFIG_HARDWARE_DEBUG_API 112 113static uint16_t convertBpNumToArch(uint16_t bp_num) 114{ 115 if (bp_num >= seL4_NumExclusiveBreakpoints) { 116 bp_num -= seL4_NumExclusiveBreakpoints; 117 } 118 return bp_num; 119} 120 121static word_t getTypeFromBpNum(uint16_t bp_num) 122{ 123 return (bp_num >= seL4_NumExclusiveBreakpoints) 124 ? seL4_DataBreakpoint 125 : seL4_InstructionBreakpoint; 126} 127 128static inline syscall_error_t Arch_decodeConfigureSingleStepping(tcb_t *t, 129 uint16_t bp_num, 130 word_t n_instr, 131 bool_t is_reply) 132{ 133 word_t type; 134 syscall_error_t ret = { 135 .type = seL4_NoError 136 }; 137 138 if (is_reply) { 139 /* If this is a single-step fault reply, just default to the already- 140 * configured bp_num. Of course, this assumes that a register had 141 * already previously been configured for single-stepping. 142 */ 143 if (!t->tcbArch.tcbContext.breakpointState.single_step_enabled) { 144 userError("Debug: Single-step reply when single-stepping not " 145 "enabled."); 146 ret.type = seL4_IllegalOperation; 147 return ret; 148 } 149 150 type = seL4_InstructionBreakpoint; 151 bp_num = t->tcbArch.tcbContext.breakpointState.single_step_hw_bp_num; 152 } else { 153 type = getTypeFromBpNum(bp_num); 154 bp_num = convertBpNumToArch(bp_num); 155 } 156 157 if (type != seL4_InstructionBreakpoint || bp_num >= seL4_FirstWatchpoint) { 158 /* Must use an instruction BP register */ 159 userError("Debug: Single-stepping can only be used with an instruction " 160 "breakpoint."); 161 ret.type = seL4_InvalidArgument; 162 ret.invalidArgumentNumber = 0; 163 return ret; 164 } 165 if (t->tcbArch.tcbContext.breakpointState.single_step_enabled == true) { 166 if (bp_num != t->tcbArch.tcbContext.breakpointState.single_step_hw_bp_num) { 167 /* Can't configure more than one register for stepping. */ 168 userError("Debug: Only one register can be configured for " 169 "single-stepping at a time."); 170 ret.type = seL4_InvalidArgument; 171 ret.invalidArgumentNumber = 0; 172 return ret; 173 } 174 } 175 176 return ret; 177} 178 179bool_t byte8WatchpointsSupported(void); 180 181static inline syscall_error_t Arch_decodeSetBreakpoint(tcb_t *t, 182 uint16_t bp_num, word_t vaddr, word_t type, 183 word_t size, word_t rw) 184{ 185 syscall_error_t ret = { 186 .type = seL4_NoError 187 }; 188 189 bp_num = convertBpNumToArch(bp_num); 190 191 if (type == seL4_DataBreakpoint) { 192 if (bp_num >= seL4_NumExclusiveWatchpoints) { 193 userError("Debug: invalid data-watchpoint number %u.", bp_num); 194 ret.type = seL4_RangeError; 195 ret.rangeErrorMin = 0; 196 ret.rangeErrorMax = seL4_NumExclusiveBreakpoints - 1; 197 return ret; 198 } 199 } else if (type == seL4_InstructionBreakpoint) { 200 if (bp_num >= seL4_NumExclusiveBreakpoints) { 201 userError("Debug: invalid instruction breakpoint nunber %u.", bp_num); 202 ret.type = seL4_RangeError; 203 ret.rangeErrorMin = 0; 204 ret.rangeErrorMax = seL4_NumExclusiveWatchpoints - 1; 205 return ret; 206 } 207 } 208 209 if (size == 8 && !byte8WatchpointsSupported()) { 210 userError("Debug: 8-byte watchpoints not supported on this CPU."); 211 ret.type = seL4_InvalidArgument; 212 ret.invalidArgumentNumber = 3; 213 return ret; 214 } 215 if (size == 8 && type != seL4_DataBreakpoint) { 216 userError("Debug: 8-byte sizes can only be used with watchpoints."); 217 ret.type = seL4_InvalidArgument; 218 ret.invalidArgumentNumber = 3; 219 return ret; 220 } 221 222 return ret; 223} 224 225static inline syscall_error_t Arch_decodeGetBreakpoint(tcb_t *t, uint16_t bp_num) 226{ 227 syscall_error_t ret = { 228 .type = seL4_NoError 229 }; 230 231 if (bp_num >= seL4_FirstWatchpoint + seL4_NumExclusiveWatchpoints) { 232 userError("Arch Debug: Invalid API bp_num %u.", bp_num); 233 ret.type = seL4_NoError; 234 return ret; 235 } 236 return ret; 237} 238 239static inline syscall_error_t Arch_decodeUnsetBreakpoint(tcb_t *t, uint16_t bp_num) 240{ 241 syscall_error_t ret = { 242 .type = seL4_NoError 243 }; 244 245 if (bp_num >= seL4_FirstWatchpoint + seL4_NumExclusiveWatchpoints) { 246 userError("Arch Debug: Invalid API bp_num %u.", bp_num); 247 ret.type = seL4_NoError; 248 return ret; 249 } 250 251 word_t type; 252 dbg_bcr_t bcr; 253 254 type = getTypeFromBpNum(bp_num); 255 bp_num = convertBpNumToArch(bp_num); 256 257 bcr.words[0] = t->tcbArch.tcbContext.breakpointState.breakpoint[bp_num].cr; 258 if (type == seL4_InstructionBreakpoint) { 259 if (Arch_breakpointIsMismatch(bcr) == true && dbg_bcr_get_enabled(bcr)) { 260 userError("Rejecting call to unsetBreakpoint on breakpoint configured " 261 "for single-stepping (hwid %u).", bp_num); 262 ret.type = seL4_IllegalOperation; 263 return ret; 264 } 265 } 266 267 return ret; 268} 269 270#endif /* CONFIG_HARDWARE_DEBUG_API */ 271 272