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