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_ARMV_DEBUG_H_
14#define __ARCH_ARMV_DEBUG_H_
15
16#include <config.h>
17
18#if defined(CONFIG_HARDWARE_DEBUG_API) || defined(CONFIG_EXPORT_PMU_USER)
19
20#include <mode/machine/debug.h>
21#include <mode/machine.h> /* MRC/MCR */
22
23/** Read DBGDSCR from CP14.
24 */
25static inline word_t
26readDscrCp(void)
27{
28    word_t v;
29
30    MRC(DBGDSCR_ext, v);
31    return v;
32}
33
34/** Write DBGDSCR (Status and control register).
35 * On ARMv7, the external view of the CP14 DBGDSCR register is preferred since
36 * the internal view is fully read-only.
37 */
38static inline void
39writeDscrCp(word_t val)
40{
41    MCR(DBGDSCR_ext, val);
42}
43#endif /* CONFIG_HARDWARE_DEBUG_API CONFIG_EXPORT_PMU_USER */
44
45#ifdef CONFIG_HARDWARE_DEBUG_API
46#define DBGVCR_RESERVED_BITS_MASK      \
47                        (BIT(5)|BIT(8)|BIT(9)|BIT(13)|BIT(16)|BIT(24)|BIT(29))
48
49#define DBGWCR_BAS_HIGH_SHIFT         (9u)
50#define DBGWCR_0 "p14,0,%0,c0,c0,7"
51
52enum v7_breakpoint_type {
53    DBGBCR_TYPE_UNLINKED_INSTRUCTION_MATCH = 0u,
54    DBGBCR_TYPE_LINKED_INSTRUCTION_MATCH = 0x1u,
55    DBGBCR_TYPE_UNLINKED_CONTEXT_MATCH = 0x2u,
56    DBGBCR_TYPE_LINKED_CONTEXT_MATCH = 0x3u,
57
58    DBGBCR_TYPE_UNLINKED_INSTRUCTION_MISMATCH = 0x4u,
59    DBGBCR_TYPE_LINKED_INSTRUCTION_MISMATCH = 0x5u,
60
61    DBGBCR_TYPE_UNLINKED_VMID_MATCH = 0x8u,
62    DBGBCR_TYPE_LINKED_VMID_MATCH = 0x9u,
63    DBGBCR_TYPE_UNLINKED_VMID_AND_CONTEXT_MATCH = 0xAu,
64    DBGBCR_TYPE_LINKED_VMID_AND_CONTEXT_MATCH = 0xBu
65};
66
67
68/** Determines whether or not 8-byte watchpoints are supported.
69 *
70 * Checks to see if the 8-byte byte-address-select high bits ignore writes.
71 */
72static inline bool_t
73watchpoint8bSupported(void)
74{
75    word_t wcrtmp;
76
77    /* ARMv7 manual: C11.11.44:
78     * "A 4-bit Byte address select field is DBGWCR[8:5]. DBGWCR[12:9] is RAZ/WI."
79     *
80     * So if 8-byte WPs aren't supported, then the higher 4-bits of the BAS
81     * field will be RAZ/WI. We can just test the first WP's BAS bits and see
82     * what happens.
83     */
84    MRC(DBGWCR_0, wcrtmp);
85    wcrtmp |= BIT(DBGWCR_BAS_HIGH_SHIFT);
86    MCR(DBGWCR_0, wcrtmp);
87
88    /* Re-read to know if the write to the bit was ignored */
89    MRC(DBGWCR_0, wcrtmp);
90    return wcrtmp & BIT(DBGWCR_BAS_HIGH_SHIFT);
91}
92
93/** Enables the debug architecture mode that allows us to receive debug events
94 * as exceptions.
95 *
96 * CPU can operate in one of 2 debug architecture modes: "halting" and
97 * "monitor". In halting mode, when a debug event occurs, the CPU will halt
98 * execution and enter a special state in which it can be examined by an
99 * external debugger dongle.
100 *
101 * In monitor mode, the CPU will deliver debug events to the kernel as
102 * exceptions. Monitor mode is what's actually useful to us. If it's not
103 * supported by the CPU, it's impossible for the API to work.
104 *
105 * Unfortunately, it's also gated behind a hardware pin signal, #DBGEN. If
106 * #DBGEN is held low, monitor mode is unavailable.
107 */
108BOOT_CODE static bool_t
109enableMonitorMode(void)
110{
111    dbg_dscr_t dscr;
112
113    dscr.words[0] = readDscrCp();
114    dscr = dbg_dscr_set_haltingDebugEnable(dscr, 0);
115    dscr = dbg_dscr_set_disableAllUserAccesses(dscr, 1);
116    dscr = dbg_dscr_set_monitorDebugEnable(dscr, 1);
117
118    writeDscrCp(dscr.words[0]);
119    isb();
120
121    /* We can tell if the #DBGEN signal is enabled by setting
122     * the DBGDSCR.MDBGEn bit. If the #DBGEN signal is not enabled, writes
123     * to DBGDSCR.MDBGEn will be ignored, and it will always read as zero.
124     *
125     * We test here to see if the DBGDSCR.MDBGEn bit is still 0, even after
126     * we set it to 1 in enableMonitorMode().
127     *
128     * ARMv6 manual, sec D3.3.2, "Monitor debug-mode enable, bit[15]":
129     *
130     *  "Monitor debug-mode has to be both selected and enabled (bit 14
131     *  clear and bit 15 set) for the core to take a Debug exception."
132     *
133     *  "If the external interface input DBGEN is low, DSCR[15:14] reads as
134     *  0b00. The programmed value is masked until DBGEN is taken high, at
135     *  which time value is read and behavior reverts to the programmed
136     *  value."
137     */
138    /* Re-read the value */
139    dscr.words[0] = readDscrCp();
140    if (dbg_dscr_get_monitorDebugEnable(dscr) == 0) {
141        printf("#DBGEN signal held low. Monitor mode unavailable.\n");
142        return false;
143    }
144    return true;
145}
146
147static inline dbg_bcr_t
148Arch_setupBcr(dbg_bcr_t in_val, bool_t is_match)
149{
150    dbg_bcr_t bcr;
151
152    bcr = dbg_bcr_set_addressMask(in_val, 0);
153    bcr = dbg_bcr_set_hypeModeControl(bcr, 0);
154    bcr = dbg_bcr_set_secureStateControl(bcr, 0);
155    if (is_match) {
156        bcr = dbg_bcr_set_breakpointType(bcr, DBGBCR_TYPE_UNLINKED_INSTRUCTION_MATCH);
157    } else {
158        bcr = dbg_bcr_set_breakpointType(bcr, DBGBCR_TYPE_UNLINKED_INSTRUCTION_MISMATCH);
159    }
160    return bcr;
161}
162
163static inline dbg_wcr_t
164Arch_setupWcr(dbg_wcr_t in_val)
165{
166    dbg_wcr_t wcr;
167
168    wcr = dbg_wcr_set_addressMask(in_val, 0);
169    wcr = dbg_wcr_set_hypeModeControl(wcr, 0);
170    wcr = dbg_wcr_set_secureStateControl(wcr, 0);
171    return wcr;
172}
173
174static inline bool_t
175Arch_breakpointIsMismatch(dbg_bcr_t in_val)
176{
177    /* Detect if the register is set up for mismatch (single-step). */
178    if (dbg_bcr_get_breakpointType(in_val) == DBGBCR_TYPE_UNLINKED_INSTRUCTION_MISMATCH) {
179        return true;
180    }
181    return false;
182}
183
184#endif /* CONFIG_HARDWARE_DEBUG_API */
185#endif /* __ARCH_ARMV_DEBUG_H_ */
186