blob: ab62298477985bcdfdebe5b80abe6e3c4df93832 [file] [log] [blame]
/*#
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
#*/
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sel4/sel4.h>
#include <sel4debug/debug.h>
#include <utils/util.h>
#include <camkes.h>
#include <stdarg.h>
#include <camkes/gdb/serial.h>
#include <camkes/gdb/gdb.h>
/*? macros.show_includes(me.instance.type.includes) ?*/
/*? macros.show_includes(me.interface.type.includes, '../static/components/%s/' % me.instance.type.name) ?*/
/*- set methods_len = len(me.interface.type.methods) -*/
/*- set instance = me.instance.name -*/
/*- set interface = me.interface.name -*/
/*- set size = 'seL4_MsgMaxLength * sizeof(seL4_Word)' -*/
/*- set allow_trailing_data = False -*/
/*- set ep = alloc("ep_fault", seL4_EndpointObject, read=True, write=True, grantreply=True) -*/
/*- set cnode = alloc_cap('cnode', my_cnode) -*/
/*- if options.realtime -*/
/*- set reply_cap_slot = alloc('reply_cap_slot', seL4_RTReplyObject) -*/
/*- else -*/
/*- set reply_cap_slot = alloc_cap('reply_cap_slot', None) -*/
/*- endif -*/
/*- set info = c_symbol('info') -*/
static gdb_state_t gdb_state;
static stop_reason_t find_stop_reason(seL4_Word fault_type, seL4_Word *args) {
if (fault_type == seL4_Fault_DebugException) {
seL4_Word exception_reason = args[1];
ZF_LOGD("MR 0: %zX", args[0]);
ZF_LOGD("MR 1: %zX", args[1]);
ZF_LOGD("MR 2: %zX", args[2]);
ZF_LOGD("MR 3: %zX", args[3]);
ZF_LOGD("Breakpoint number %zu", args[1]);
if (exception_reason == seL4_DataBreakpoint) {
ZF_LOGD("Data breakpoint");
gdb_state.stop_watch_addr = args[2];
return stop_watch;
} else if (exception_reason == seL4_InstructionBreakpoint) {
ZF_LOGD("Hardware breakpoint");
return stop_hw_break;
} else if (exception_reason == seL4_SingleStep && gdb_state.current_thread_step_mode) {
return stop_step;
} else if (exception_reason == seL4_SoftwareBreakRequest) {
ZF_LOGD("Decrementing fault ep because of seL4 kernel behavior");
/* TODO This is a special case where the seL4 kernel gives us a fault instruction
pointer that is no the faulting instruction. We decrement the pc to correct for
this. In the future the kernel may end up changing the fault ip behavior and this
decrement will then need to be removed to account for it.
*/
gdb_state.current_pc--;
delegate_write_register(gdb_state.current_thread_tcb, gdb_state.current_pc, 0);
return stop_sw_break;
} else {
return stop_none;
}
} else {
ZF_LOGE("Unknown fault type: %zd", fault_type);
ZF_LOGE("MR 0: %zX", args[0]);
ZF_LOGE("MR 1: %zX", args[1]);
ZF_LOGE("MR 2: %zX", args[2]);
ZF_LOGE("MR 3: %zX", args[3]);
return stop_none;
}
}
void /*? me.interface.name ?*/__init(void) {
gdb_state.sem_post = b_post;
gdb_state.current_thread_tcb = 1;
serial_init(&gdb_state);
}
int /*? me.interface.name ?*/__run(void) {
seL4_Word fault_type;
seL4_Word length;
seL4_MessageInfo_t info;
seL4_Word args[4];
seL4_Word reply_cap = /*? reply_cap_slot ?*/;
while (1) {
/* Wait for fault */
info = seL4_Recv(/*? ep ?*/, &gdb_state.current_thread_tcb);
/* Get the relevant registers */
fault_type = seL4_MessageInfo_get_label(info);
length = seL4_MessageInfo_get_length(info);
for (int i = 0; i < length; i++) {
args[i] = seL4_GetMR(i);
}
gdb_state.current_pc = args[0];
ZF_LOGD("------------------------------");
ZF_LOGD("Received fault for tcb %zu", gdb_state.current_thread_tcb);
ZF_LOGD("Stopped at %zx", gdb_state.current_pc);
ZF_LOGD("Length: %zu", length);
// Save the reply cap
seL4_CNode_SaveCaller(/*? cnode ?*/, reply_cap, 32);
gdb_state.stop_reason = find_stop_reason(fault_type, args);
gdb_state.current_thread_step_mode = false;
/* Send fault message to gdb client */
gdb_handle_fault(&gdb_state);
/* Wait for gdb client to deal with fault */
int UNUSED error = b_wait();
/* Reply to the fault ep to restart the thread.
We look inside the gdb_state struct to interpret how to restart the thread.
*/
if (gdb_state.stop_reason == stop_step && gdb_state.current_thread_step_mode==false) {
/* If this was a Debug Exception, then we respond with
a bp_num and the number of instruction to step
Since we're going to continue, we set MR0 to 0
*/
info = seL4_MessageInfo_new(0, 0, 0, 1);
seL4_SetMR(0, 0);
seL4_Send(reply_cap, info);
} else if (gdb_state.stop_reason == stop_none) {
/* If this was a fault, set the instruction pointer to
what we expect it to be
*/
info = seL4_MessageInfo_new(0, 0, 0, 1);
seL4_SetMR(0, gdb_state.current_pc);
seL4_Send(reply_cap, info);
} else {
ZF_LOGD("Responding to some other debug exception %d", gdb_state.stop_reason);
seL4_Signal(reply_cap);
}
}
UNREACHABLE();
}