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#ifdef CONFIG_HARDWARE_DEBUG_API
18
19#define DBGVCR_RESERVED_BITS_MASK      (0xFFFFFFF0|BIT(5))
20
21enum v6_breakpoint_meaning /* BCR[22:21] */ {
22    DBGBCR_V6MEANING_INSTRUCTION_VADDR_MATCH = 0u,
23    DBGBCR_V6MEANING_CONTEXT_ID_MATCH = 1u,
24    DBGBCR_V6MEANING_INSTRUCTION_VADDR_MISMATCH = 2u
25};
26
27/** Read DBGDSCR from CP14.
28 *
29 * DBGDSCR_ext (external view) is not exposed on debug v6. Accessing it on
30 * v6 triggers an #UNDEFINED abort.
31 */
32static word_t
33readDscrCp(void)
34{
35    word_t v;
36
37    MRC(DBGDSCR_int, v);
38    return v;
39}
40
41/** Write DBGDSCR (Status and control register).
42 *
43 * On ARMv6, there is no mmapping, and the coprocessor doesn't expose an
44 * external vs internal view of DSCR. There's only the internal, but the MDBGEn
45 * but is RW (as opposed to V7 where the internal MDBGEn is RO).
46 *
47 * Even so, the KZM still ignores our writes anyway *shrug*.
48 */
49static void
50writeDscrCp(word_t val)
51{
52    MCR(DBGDSCR_int, val);
53}
54
55/** Determines whether or not 8-byte watchpoints are supported.
56 */
57static inline bool_t
58watchpoint8bSupported(void)
59{
60    /* V6 doesn't support 8B watchpoints. */
61    return false;
62}
63
64/** Enables the debug architecture mode that allows us to receive debug events
65 * as exceptions.
66 *
67 * CPU can operate in one of 2 debug architecture modes: "halting" and
68 * "monitor". In halting mode, when a debug event occurs, the CPU will halt
69 * execution and enter a special state in which it can be examined by an
70 * external debugger dongle.
71 *
72 * In monitor mode, the CPU will deliver debug events to the kernel as
73 * exceptions. Monitor mode is what's actually useful to us. If it's not
74 * supported by the CPU, it's impossible for the API to work.
75 *
76 * Unfortunately, it's also gated behind a hardware pin signal, #DBGEN. If
77 * #DBGEN is held low, monitor mode is unavailable.
78 */
79BOOT_CODE static bool_t
80enableMonitorMode(void)
81{
82    dbg_dscr_t dscr;
83
84    dscr.words[0] = readDscrCp();
85    /* HDBGEn is read-only on v6 debug. */
86    if (dbg_dscr_get_haltingDebugEnable(dscr) != 0) {
87        printf("Halting debug is enabled, and can't be disabled. Monitor mode "
88               "unavailable.\n");
89        return false;
90    }
91
92    dscr = dbg_dscr_set_monitorDebugEnable(dscr, 1);
93    writeDscrCp(dscr.words[0]);
94    isb();
95
96    /* On V6 debug, we can tell if the #DBGEN signal is enabled by setting
97     * the DBGDSCR.MDBGEn bit. If the #DBGEN signal is not enabled, writes
98     * to DBGDSCR.MDBGEn will be ignored, and it will always read as zero.
99     *
100     * We test here to see if the DBGDSCR.MDBGEn bit is still 0, even after
101     * we set it to 1 in enableMonitorMode().
102     *
103     * ARMv6 manual, sec D3.3.2, "Monitor debug-mode enable, bit[15]":
104     *
105     *  "Monitor debug-mode has to be both selected and enabled (bit 14
106     *  clear and bit 15 set) for the core to take a Debug exception."
107     *
108     *  "If the external interface input DBGEN is low, DSCR[15:14] reads as
109     *  0b00. The programmed value is masked until DBGEN is taken high, at
110     *  which time value is read and behavior reverts to the programmed
111     *  value."
112     */
113    /* Re-read the value */
114    dscr.words[0] = readDscrCp();
115    if (dbg_dscr_get_monitorDebugEnable(dscr) == 0) {
116        printf("#DBGEN signal held low. Monitor mode unavailable.\n");
117        return false;
118    }
119    return true;
120}
121
122static inline dbg_bcr_t
123Arch_setupBcr(dbg_bcr_t in_val, bool_t is_match)
124{
125    dbg_bcr_t bcr;
126
127    if (is_match) {
128        bcr = dbg_bcr_set_meaning(in_val, DBGBCR_V6MEANING_INSTRUCTION_VADDR_MATCH);
129    } else {
130        bcr = dbg_bcr_set_meaning(in_val, DBGBCR_V6MEANING_INSTRUCTION_VADDR_MISMATCH);
131    }
132    bcr = dbg_bcr_set_enableLinking(bcr, 0);
133    return bcr;
134}
135
136static inline dbg_wcr_t
137Arch_setupWcr(dbg_wcr_t in_val)
138{
139    return in_val;
140}
141
142static inline bool_t
143Arch_breakpointIsMismatch(dbg_bcr_t in_val)
144{
145    return dbg_bcr_get_meaning(in_val) == DBGBCR_V6MEANING_INSTRUCTION_VADDR_MISMATCH;
146}
147
148#endif /* CONFIG_HARDWARE_DEBUG_API */
149#endif /* __ARCH_ARMV_DEBUG_H_ */
150