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