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#ifdef CONFIG_HARDWARE_DEBUG_API 11 12#include <types.h> 13#include <api/types.h> 14#include <arch/machine/registerset.h> 15#include <mode/machine/debug.h> 16 17/* Bit in DR7 that will enable each BP respectively. */ 18#define X86_DEBUG_BP0_ENABLE_BIT ((word_t)BIT(1)) 19#define X86_DEBUG_BP1_ENABLE_BIT ((word_t)BIT(3)) 20#define X86_DEBUG_BP2_ENABLE_BIT ((word_t)BIT(5)) 21#define X86_DEBUG_BP3_ENABLE_BIT ((word_t)BIT(7)) 22 23/** Per-thread initial state setting. 24 * 25 * The most significant thing done here is that we pre-load reserved bits from 26 * the hardware registers into the TCB context. 27 * 28 * @param context TCB breakpoint context for the thread being initialized. 29 */ 30void Arch_initBreakpointContext(user_breakpoint_state_t *context); 31 32/** Discerns and handles a debug exception. 33 * 34 * Determines which hardware breakpoint triggered a debug exception, and 35 * generates a message to userspace for that breakpoint exception, or generates 36 * a message to userspace for a single-step exception, if it was a single-step 37 * event that triggered the exception. 38 * 39 * ARM's exception-path flow works differently. 40 * 41 * @param int_vector Processor-level vector number on which the exception 42 * occurred. May be 1 or 3, depending on whether the exception 43 * is a breakpoint, single-step, or INT3 exception. 44 */ 45exception_t handleUserLevelDebugException(int int_vector); 46 47/** These next two functions are part of some state flags. 48 * 49 * A bitfield of all currently enabled breakpoints for a thread is kept in that 50 * thread's TCB. These two functions here set and unset the bits in that 51 * bitfield. 52 */ 53static inline void setBreakpointUsedFlag(tcb_t *t, uint16_t bp_num) 54{ 55 if (t != NULL) { 56 t->tcbArch.tcbContext.breakpointState.used_breakpoints_bf |= BIT(bp_num); 57 } 58} 59 60static inline void unsetBreakpointUsedFlag(tcb_t *t, uint16_t bp_num) 61{ 62 if (t != NULL) { 63 t->tcbArch.tcbContext.breakpointState.used_breakpoints_bf &= ~BIT(bp_num); 64 } 65} 66 67/** Program the debug registers with values that will disable all breakpoints. 68 * 69 * This is an optimization for threads that don't use any breakpoints: we won't 70 * try to pop all the context from a block of memory, but just unset all the 71 * "enable" bits in the registers. 72 * @param at arch_tcb_t from which the reserved bits will be loaded before 73 * setting the disable bits. 74 */ 75static void loadAllDisabledBreakpointState(tcb_t *t) 76{ 77 word_t disable_value; 78 79 disable_value = t->tcbArch.tcbContext.breakpointState.dr[5]; 80 disable_value &= ~(X86_DEBUG_BP0_ENABLE_BIT | X86_DEBUG_BP1_ENABLE_BIT 81 | X86_DEBUG_BP2_ENABLE_BIT | X86_DEBUG_BP3_ENABLE_BIT); 82 83 writeDr7Reg(disable_value); 84} 85 86static inline void restore_user_debug_context(tcb_t *target_thread) 87{ 88 arch_tcb_t *uds = &target_thread->tcbArch; 89 if (uds->tcbContext.breakpointState.used_breakpoints_bf != 0) { 90 loadBreakpointState(target_thread); 91 } else { 92 loadAllDisabledBreakpointState(target_thread); 93 } 94 95 /* If single-stepping was enabled, we need to re-set the TF flag as well. */ 96 if (uds->tcbContext.breakpointState.single_step_enabled == true) { 97 uds->tcbContext.registers[FLAGS] |= FLAGS_TF; 98 /* Under ia32 we also need to ensure we return via an IRET as the 99 * sysexit return path will pop flags a couple of instructions 100 * before performing sysexit resulting in an exception, due to 101 * single stepping, inside the kernel. To avoid this we will 102 * return via an IRET, which atomically pops the flags and 103 * returns to user level */ 104#ifdef CONFIG_ARCH_IA32 105 if (getRegister(target_thread, Error) == -1) { 106 setRegister(target_thread, Error, 0); 107 /* As we did not come in from an interrupt there is no guarantee 108 * the CS and SS in the context are set to anything sensible, so 109 * force them to the correct user value */ 110 setRegister(target_thread, CS, SEL_CS_3); 111 setRegister(target_thread, SS, SEL_DS_3); 112 } 113#endif 114 } 115} 116 117static inline syscall_error_t Arch_decodeConfigureSingleStepping(tcb_t *t, 118 uint16_t bp_num, 119 word_t n_instr, 120 bool_t is_reply) 121{ 122 syscall_error_t ret; 123 124 ret.type = seL4_NoError; 125 return ret; 126} 127 128bool_t byte8BreakpointsSupported(void); 129 130static inline syscall_error_t Arch_decodeSetBreakpoint(tcb_t *t, 131 uint16_t bp_num, word_t vaddr, word_t types, 132 word_t size, word_t rw) 133{ 134 syscall_error_t ret = { 135 .type = seL4_NoError 136 }; 137 138 if (bp_num >= X86_DEBUG_BP_N_REGS) { 139 userError("Debug: invalid bp_num %u.", bp_num); 140 ret.rangeErrorMin = 0; 141 ret.rangeErrorMax = 3; 142 ret.type = seL4_RangeError; 143 return ret; 144 } 145 if (size == 8 && !byte8BreakpointsSupported()) { 146 userError("Debug: 8-byte breakpoints/watchpoints unsupported on this CPU."); 147 ret.invalidArgumentNumber = 3; 148 ret.type = seL4_InvalidArgument; 149 return ret; 150 } 151 return ret; 152} 153 154static inline syscall_error_t Arch_decodeGetBreakpoint(tcb_t *t, uint16_t bp_num) 155{ 156 syscall_error_t ret = { 157 .type = seL4_NoError 158 }; 159 160 if (bp_num >= X86_DEBUG_BP_N_REGS) { 161 userError("Debug: invalid bp_num %u.", bp_num); 162 ret.rangeErrorMin = 0; 163 ret.rangeErrorMax = 3; 164 ret.type = seL4_RangeError; 165 } 166 return ret; 167} 168 169static inline syscall_error_t Arch_decodeUnsetBreakpoint(tcb_t *t, uint16_t bp_num) 170{ 171 syscall_error_t ret = { 172 .type = seL4_NoError 173 }; 174 175 if (bp_num >= X86_DEBUG_BP_N_REGS) { 176 userError("Debug: invalid bp_num %u.", bp_num); 177 ret.rangeErrorMin = 0; 178 ret.rangeErrorMax = 3; 179 ret.type = seL4_RangeError; 180 } 181 return ret; 182} 183 184#endif /* CONFIG_HARDWARE_DEBUG_API */ 185