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#include <assert.h>
9
10#ifdef CONFIG_HARDWARE_DEBUG_API
11
12#define X86_DEBUG_BP_N_REGS                 (4)
13
14static inline word_t readDr6Reg(void)
15{
16    word_t ret;
17
18    asm volatile(
19        "movl %%dr6, %0 \n\t"
20        : "=r"(ret));
21    return ret;
22}
23
24static inline void writeDr6Reg(word_t val)
25{
26    asm volatile(
27        "movl %0, %%dr6 \n\t"
28        :
29        : "r"(val));
30}
31
32static inline word_t readDr7Reg(void)
33{
34    word_t ret;
35
36    asm volatile(
37        "movl %%dr7, %0 \n\t"
38        : "=r"(ret));
39    return ret;
40}
41
42static inline void writeDr7Reg(word_t val)
43{
44    asm volatile(
45        "movl %0, %%dr7 \n\t"
46        :
47        : "r"(val));
48}
49
50static inline word_t readDrReg(uint8_t reg)
51{
52    word_t ret;
53
54    assert(reg < X86_DEBUG_BP_N_REGS);
55    switch (reg) {
56    case 0:
57        asm volatile("movl %%dr0, %0 \n\t" : "=r"(ret));
58        break;
59    case 1:
60        asm volatile("movl %%dr1, %0 \n\t" : "=r"(ret));
61        break;
62    case 2:
63        asm volatile("movl %%dr2, %0 \n\t" : "=r"(ret));
64        break;
65    default:
66        asm volatile("movl %%dr3, %0 \n\t" : "=r"(ret));
67        break;
68    }
69    return ret;
70}
71
72static inline void writeDrReg(uint8_t reg, word_t val)
73{
74    assert(reg < X86_DEBUG_BP_N_REGS);
75    switch (reg) {
76    case 0:
77        asm volatile("movl %0, %%dr0 \n\t" :: "r"(val));
78        break;
79    case 1:
80        asm volatile("movl %0, %%dr1 \n\t" :: "r"(val));
81        break;
82    case 2:
83        asm volatile("movl %0, %%dr2 \n\t" :: "r"(val));
84        break;
85    default:
86        asm volatile("movl %0, %%dr3 \n\t" :: "r"(val));
87        break;
88    }
89}
90
91/** Restore debug register context from a block of memory.
92 *@param source The memory block from which to load the register values.
93 */
94static inline void loadBreakpointState(tcb_t *source)
95{
96    /* Order does matter when restoring the registers: we want to restore the
97     * breakpoint control register (DR7) last since it is what "activates" the
98     * effects of the configuration described by the other registers.
99     */
100    asm volatile(
101        "movl %0, %%edx \n\t"
102        "movl (%%edx), %%ecx \n\t"
103        "movl %%ecx, %%dr0 \n\t"
104        "addl $4, %%edx \n\t"
105        "movl (%%edx), %%ecx \n\t"
106        "movl %%ecx, %%dr1 \n\t"
107        "addl $4, %%edx \n\t"
108        "movl (%%edx), %%ecx \n\t"
109        "movl %%ecx, %%dr2 \n\t"
110        "addl $4, %%edx \n\t"
111        "movl (%%edx), %%ecx \n\t"
112        "movl %%ecx, %%dr3 \n\t"
113        "addl $4, %%edx \n\t"
114        "movl (%%edx), %%ecx \n\t"
115        "movl %%ecx, %%dr6 \n\t"
116        "addl $4, %%edx \n\t"
117        "movl (%%edx), %%ecx \n\t"
118        "movl %%ecx, %%dr7 \n\t"
119        :
120        : "r"(source->tcbArch.tcbContext.breakpointState.dr)
121        : "edx", "ecx");
122}
123
124#endif /* CONFIG_HARDWARE_DEBUG_API */
125