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 <assert.h>
14#include <limits.h>
15#include <stddef.h>
16#include <stdint.h>
17#include <stdlib.h>
18#include <string.h>
19#include <sel4/sel4.h>
20#include <sel4debug/debug.h>
21#include <utils/util.h>
22#include <camkes.h>
23#include <stdarg.h>
24#include <camkes/gdb/serial.h>
25#include <camkes/gdb/gdb.h>
26
27/*? macros.show_includes(me.instance.type.includes) ?*/
28/*? macros.show_includes(me.interface.type.includes, '../static/components/%s/' % me.instance.type.name) ?*/
29
30/*- set methods_len = len(me.interface.type.methods) -*/
31/*- set instance = me.instance.name -*/
32/*- set interface = me.interface.name -*/
33/*- set size = 'seL4_MsgMaxLength * sizeof(seL4_Word)' -*/
34/*- set allow_trailing_data = False -*/
35/*- set ep = alloc("ep_fault", seL4_EndpointObject, read=True, write=True, grantreply=True) -*/
36/*- set cnode = alloc_cap('cnode', my_cnode) -*/
37    /*- if options.realtime -*/
38        /*- set reply_cap_slot = alloc('reply_cap_slot', seL4_RTReplyObject) -*/
39    /*- else -*/
40        /*- set reply_cap_slot = alloc_cap('reply_cap_slot', None) -*/
41    /*- endif -*/
42/*- set info = c_symbol('info') -*/
43
44static gdb_state_t gdb_state;
45
46static stop_reason_t find_stop_reason(seL4_Word fault_type, seL4_Word *args) {
47    if (fault_type == seL4_Fault_DebugException) {
48        seL4_Word exception_reason = args[1];
49        ZF_LOGD("MR 0: %zX", args[0]);
50        ZF_LOGD("MR 1: %zX", args[1]);
51        ZF_LOGD("MR 2: %zX", args[2]);
52        ZF_LOGD("MR 3: %zX", args[3]);
53        ZF_LOGD("Breakpoint number %zu", args[1]);
54        if (exception_reason == seL4_DataBreakpoint) {
55            ZF_LOGD("Data breakpoint");
56            gdb_state.stop_watch_addr = args[2];
57            return stop_watch;
58        } else if (exception_reason == seL4_InstructionBreakpoint) {
59            ZF_LOGD("Hardware breakpoint");
60            return stop_hw_break;
61        } else if (exception_reason == seL4_SingleStep && gdb_state.current_thread_step_mode) {
62            return stop_step;
63        } else if (exception_reason == seL4_SoftwareBreakRequest) {
64            ZF_LOGD("Decrementing fault ep because of seL4 kernel behavior");
65            /* TODO This is a special case where the seL4 kernel gives us a fault instruction
66               pointer that is no the faulting instruction.  We decrement the pc to correct for
67               this.  In the future the kernel may end up changing the fault ip behavior and this
68               decrement will then need to be removed to account for it.
69             */
70            gdb_state.current_pc--;
71            delegate_write_register(gdb_state.current_thread_tcb, gdb_state.current_pc, 0);
72
73            return stop_sw_break;
74        } else {
75            return stop_none;
76        }
77    } else {
78        ZF_LOGE("Unknown fault type: %zd", fault_type);
79        ZF_LOGE("MR 0: %zX", args[0]);
80        ZF_LOGE("MR 1: %zX", args[1]);
81        ZF_LOGE("MR 2: %zX", args[2]);
82        ZF_LOGE("MR 3: %zX", args[3]);
83
84        return stop_none;
85    }
86}
87
88void /*? me.interface.name ?*/__init(void) {
89    gdb_state.sem_post = b_post;
90    gdb_state.current_thread_tcb = 1;
91    serial_init(&gdb_state);
92}
93
94int /*? me.interface.name ?*/__run(void) {
95    seL4_Word fault_type;
96    seL4_Word length;
97    seL4_MessageInfo_t info;
98    seL4_Word args[4];
99    seL4_Word reply_cap = /*? reply_cap_slot ?*/;
100    while (1) {
101        /* Wait for fault */
102        info = seL4_Recv(/*? ep ?*/, &gdb_state.current_thread_tcb);
103        /* Get the relevant registers */
104        fault_type = seL4_MessageInfo_get_label(info);
105        length = seL4_MessageInfo_get_length(info);
106        for (int i = 0; i < length; i++) {
107            args[i] = seL4_GetMR(i);
108        }
109        gdb_state.current_pc = args[0];
110        ZF_LOGD("------------------------------");
111        ZF_LOGD("Received fault for tcb %zu", gdb_state.current_thread_tcb);
112        ZF_LOGD("Stopped at %zx", gdb_state.current_pc);
113        ZF_LOGD("Length: %zu", length);
114        // Save the reply cap
115        seL4_CNode_SaveCaller(/*? cnode ?*/, reply_cap, 32);
116
117        gdb_state.stop_reason = find_stop_reason(fault_type, args);
118        gdb_state.current_thread_step_mode = false;
119
120        /* Send fault message to gdb client */
121        gdb_handle_fault(&gdb_state);
122
123        /* Wait for gdb client to deal with fault */
124        int UNUSED error = b_wait();
125
126        /* Reply to the fault ep to restart the thread.
127           We look inside the gdb_state struct to interpret how to restart the thread.
128         */
129        if (gdb_state.stop_reason == stop_step && gdb_state.current_thread_step_mode==false) {
130            /* If this was a Debug Exception, then we respond with
131               a bp_num and the number of instruction to step
132               Since we're going to continue, we set MR0 to 0
133             */
134            info = seL4_MessageInfo_new(0, 0, 0, 1);
135            seL4_SetMR(0, 0);
136            seL4_Send(reply_cap, info);
137        } else if (gdb_state.stop_reason == stop_none) {
138            /* If this was a fault, set the instruction pointer to
139               what we expect it to be
140             */
141            info = seL4_MessageInfo_new(0, 0, 0, 1);
142            seL4_SetMR(0, gdb_state.current_pc);
143            seL4_Send(reply_cap, info);
144        } else {
145            ZF_LOGD("Responding to some other debug exception %d", gdb_state.stop_reason);
146            seL4_Signal(reply_cap);
147        }
148
149    }
150    UNREACHABLE();
151}
152
153