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