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