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