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