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 BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <camkes.h>
14
15#include <string.h>
16
17typedef struct breakpoint_state {
18    seL4_Word vaddr;
19    seL4_Word type;
20    seL4_Word size;
21    seL4_Word rw;
22    seL4_Bool is_enabled;
23} breakpoint_state_t;
24
25
26static int check_read_memory(seL4_Word addr) {
27    // TODO This doesn't deal with page boundaries
28    uint32_t result = 0;
29    asm volatile (
30        "movl $0, %%eax;"
31        "movl %[addr], %%ebx;"
32        "movl (%%ebx), %%ecx;"
33        // The code above will cause a fault to the mem handler if
34        // the memory is not readable. The mem handler will set eax
35        // appropriately
36        "movl %%eax, %0;"
37        : "=r" (result)
38        : [addr] "r" (addr)
39        : "eax", "ebx", "ecx"
40    );
41    return result;
42}
43
44static int check_write_memory(seL4_Word addr) {
45    // TODO This doesn't deal with page boundaries
46    uint32_t result = 0;
47    asm volatile (
48        "movl $0, %%eax;"
49        "movl %[addr], %%ebx;"
50        "movl (%%ebx), %%ecx;"
51        "movl %%ecx, (%%ebx);"
52        // The code above will cause a fault to the mem handler if
53        // the memory is not readable. The mem handler will set eax
54        // appropriately
55        "movl %%eax, %0;"
56        : "=r" (result)
57        : [addr] "r" (addr)
58        : "eax", "ebx", "ecx"
59    );
60    return result;
61}
62
63#ifdef CONFIG_HARDWARE_DEBUG_API
64breakpoint_state_t breakpoints[seL4_NumDualFunctionMonitors];
65#endif
66
67
68static void breakpoint_init(void) {
69#ifdef CONFIG_HARDWARE_DEBUG_API
70    for (int i = 0; i < seL4_NumDualFunctionMonitors; i++) {
71        breakpoints[i].vaddr = 0;
72        breakpoints[i].type = 0;
73        breakpoints[i].size = 0;
74        breakpoints[i].rw = 0;
75        breakpoints[i].is_enabled = false;
76    }
77#endif
78}
79
80static int find_free_breakpoint(void) {
81#ifdef CONFIG_HARDWARE_DEBUG_API
82    int bp = -1;
83    for (int i = 0; i < seL4_NumDualFunctionMonitors; i++) {
84        if (!breakpoints[i].is_enabled) {
85            bp = i;
86            break;
87        }
88    }
89    return bp;
90#else
91    return -1;
92#endif
93}
94
95static int get_breakpoint_num(seL4_Word vaddr UNUSED, seL4_Word type UNUSED,
96                              seL4_Word size UNUSED, seL4_Word rw UNUSED) {
97#ifdef CONFIG_HARDWARE_DEBUG_API
98    int bp = -1;
99    for (int i = 0; i < seL4_NumDualFunctionMonitors; i++) {
100        if (breakpoints[i].is_enabled && breakpoints[i].vaddr == vaddr &&
101            breakpoints[i].type == type && breakpoints[i].size == size &&
102            breakpoints[i].rw == rw) {
103           bp = i;
104           break;
105        }
106    }
107    return bp;
108#else
109    return -1;
110#endif
111}
112
113static void set_breakpoint_state(seL4_Word vaddr UNUSED, seL4_Word type UNUSED,
114                                 seL4_Word size UNUSED, seL4_Word rw UNUSED, int bp UNUSED) {
115#ifdef CONFIG_HARDWARE_DEBUG_API
116    breakpoints[bp].vaddr = vaddr;
117    breakpoints[bp].type = type;
118    breakpoints[bp].size = size;
119    breakpoints[bp].rw = rw;
120    breakpoints[bp].is_enabled = true;
121#endif
122}
123
124static void clear_breakpoint_state(int bp UNUSED) {
125#ifdef CONFIG_HARDWARE_DEBUG_API
126    breakpoints[bp].vaddr = 0;
127    breakpoints[bp].type = 0;
128    breakpoints[bp].size = 0;
129    breakpoints[bp].rw = 0;
130    breakpoints[bp].is_enabled = false;
131#endif
132}
133
134
135void delegate__init() {
136    breakpoint_init();
137}
138
139int delegate_read_memory(seL4_Word addr, seL4_Word length, delegate_mem_range_t *data) {
140    if (length == 0 || length > MAX_MEM_RANGE) {
141        ZF_LOGE("Invalid length %d", length);
142        return 1;
143    }
144    if (check_read_memory(addr)) {
145        return 1;
146    } else {
147        memcpy(data->data, (void *)addr, length);
148        return 0;
149    }
150}
151
152int delegate_write_memory(seL4_Word addr, seL4_Word length, delegate_mem_range_t data) {
153    if (length == 0 || length > MAX_MEM_RANGE) {
154        ZF_LOGE("Invalid length %d", length);
155        return 1;
156    }
157    // If invalid memory, return with error
158    if (check_write_memory(addr)) {
159        return 1;
160    } else {
161        memcpy((void *) addr, (void *) data.data, length);
162        return 0;
163    }
164}
165
166void delegate_read_registers(seL4_Word tcb_cap, seL4_UserContext *registers) {
167    int num_regs = sizeof(seL4_UserContext) / sizeof(seL4_Word);
168    seL4_TCB_ReadRegisters(tcb_cap, false, 0, num_regs, registers);
169
170    return;
171}
172
173void delegate_read_register(seL4_Word tcb_cap, seL4_Word *reg, seL4_Word reg_num) {
174    int num_regs = sizeof(seL4_UserContext) / sizeof(seL4_Word);
175    if (reg_num >= num_regs) {
176        ZF_LOGE("Invalid register num: %zd", reg_num);
177        return;
178    }
179    seL4_UserContext regs = {0};
180    seL4_TCB_ReadRegisters(tcb_cap, false, 0, num_regs, &regs);
181    seL4_Word *reg_word = (seL4_Word *) (& regs);
182    *reg = reg_word[reg_num];
183}
184
185 int delegate_write_registers(seL4_Word tcb_cap, seL4_UserContext registers, int len) {
186    // Get register values from IPC
187    seL4_UserContext regs = {0};
188    int num_regs = sizeof(seL4_UserContext) / sizeof(seL4_Word);
189    seL4_TCB_ReadRegisters(tcb_cap, false, 0, num_regs, &regs);
190    seL4_Word *reg_word = (seL4_Word *) (&regs);
191    for (int i = 0; i < len; i++) {
192        reg_word[i] = ((seL4_Word *)&regs)[i];
193    }
194    // Write registers
195    int err = seL4_TCB_WriteRegisters(tcb_cap, false, 0, num_regs, &regs);
196    if (err) {
197        return 1;
198    } else {
199        return 0;
200    }
201}
202
203int delegate_write_register(seL4_Word tcb_cap, seL4_Word data, seL4_Word reg_num) {
204    int num_regs = sizeof(seL4_UserContext) / sizeof(seL4_Word);
205    seL4_UserContext regs = {0};
206    seL4_Word *reg_word = (seL4_Word *) (&regs);
207    seL4_TCB_ReadRegisters(tcb_cap, false, 0, num_regs, &regs);
208    // Change relevant register
209    reg_word[reg_num] = data;
210    int err = seL4_TCB_WriteRegisters(tcb_cap, false, 0, num_regs, &regs);
211    if (err) {
212        return 1;
213    } else {
214        return 0;
215    }
216
217}
218
219int delegate_insert_break(seL4_Word tcb_cap, seL4_Word type, seL4_Word addr, seL4_Word size, seL4_Word rw) {
220    int err = 0;
221#ifdef CONFIG_HARDWARE_DEBUG_API
222    // Insert the breakpoint
223    int bp_num = find_free_breakpoint();
224    if (bp_num == -1) {
225        err = 1;
226    } else {
227        // Set the breakpoint
228        err = seL4_TCB_SetBreakpoint(tcb_cap, (seL4_Uint16) bp_num, addr,
229                                     type, size, rw);
230        if (!err) {
231            set_breakpoint_state(addr, type, size, rw, bp_num);
232        }
233        seL4_TCB_GetBreakpoint(tcb_cap, bp_num);
234    }
235#else
236    err = -1;
237#endif
238    if (err) {
239        return 1;
240    } else {
241        return 0;
242    }
243}
244
245int delegate_remove_break(seL4_Word tcb_cap, seL4_Word type, seL4_Word addr, seL4_Word size, seL4_Word rw) {
246    int err = 0;
247#ifdef CONFIG_HARDWARE_DEBUG_API
248    // Find the breakpoint
249    int bp_num = get_breakpoint_num(addr, type, size, rw);
250    if (bp_num == -1) {
251        err = 1;
252    }
253    // Unset the breakpoint
254    err = seL4_TCB_UnsetBreakpoint(tcb_cap, (seL4_Uint16) bp_num);
255    if (!err) {
256        clear_breakpoint_state(bp_num);
257    }
258#else
259    err = -1;
260#endif
261    if (err) {
262        return 1;
263    } else {
264        return 0;
265    }
266}
267
268
269int delegate_resume(seL4_Word tcb_cap) {
270#ifdef CONFIG_HARDWARE_DEBUG_API
271    seL4_TCB_ConfigureSingleStepping_t result =
272        seL4_TCB_ConfigureSingleStepping(tcb_cap, 0, 0);
273
274    if (result.error) {
275        return 1;
276    } else {
277        return 0;
278    }
279#endif
280    return 1;
281}
282
283int delegate_step(seL4_Word tcb_cap) {
284#ifdef CONFIG_HARDWARE_DEBUG_API
285    seL4_TCB_ConfigureSingleStepping_t result =
286        seL4_TCB_ConfigureSingleStepping(tcb_cap, 0, 1);
287
288    if (result.error) {
289        return 1;
290    } else {
291        return 0;
292    }
293#endif
294    return 1;
295
296}
297
298