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