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#include <autoconf.h> 13 14#ifdef CONFIG_HARDWARE_DEBUG_API 15 16#include <sel4/sel4.h> 17#include <utils/attribute.h> 18 19#include <arch/debug.h> 20#include "../../../helpers.h" 21#include "../../../test.h" 22#include "../../../tests/breakpoints.h" 23 24/* Single-step test is currently only for x86. ARM requires some extra 25 * components before it can be usable. 26 */ 27static volatile int counter; 28 29NO_INLINE static void stop_point(void) 30{ 31 /* Counter is volatile so the compiler cannot optimize this away */ 32 counter = counter; 33} 34 35NO_INLINE static void single_step_guinea_pig(void) 36{ 37 for (; counter < SINGLESTEP_TEST_MAX_LOOP_ITERATIONS; counter++) { 38 /* This syscall is inserted here to ensure that syscalls are being 39 * stepped over successfully on x86. On ARM, we can just disable single- 40 * stepping in PL1 and PL2 altogether, so the problem doesn't arise. 41 */ 42 seL4_Yield(); 43 } 44 stop_point(); 45} 46 47int debugger_main(seL4_Word a0, seL4_Word reply, seL4_Word a2, seL4_Word a3) 48{ 49 seL4_CPtr debuggee_tcb_cap = a0; 50 seL4_Word badge; 51 seL4_MessageInfo_t tag; 52 seL4_TCB_ConfigureSingleStepping_t result; 53 54 /* The main thread sets a breakpoint in the debuggee thread, and this thread 55 * will get a fault on the fault EP. At that point we can enable 56 * single-stepping on the debuggee thread. 57 */ 58 tag = api_wait(fault_ep_cspath.capPtr, &badge); 59 60 if (seL4_MessageInfo_get_label(tag) != seL4_Fault_DebugException) { 61 ZF_LOGE("debugger: Got unexpected fault %zd.\n", 62 seL4_MessageInfo_get_label(tag)); 63 return -1; 64 } 65 66 fault_data.vaddr = seL4_GetMR(seL4_DebugException_FaultIP); 67 fault_data.reason = seL4_GetMR(seL4_DebugException_ExceptionReason); 68 fault_data.bp_num = seL4_GetMR(seL4_DebugException_BreakpointNumber); 69 if (fault_data.reason != seL4_InstructionBreakpoint 70 || fault_data.vaddr != (seL4_Word)&single_step_guinea_pig) { 71 ZF_LOGE("debugger: debug exception not triggered by expected conditions.\n"); 72 return -1; 73 } 74 75 /* So now we can set the single stepping going. */ 76 ZF_LOGV("debugger: Instruction BP triggered: beginning single-stepping.\n"); 77 seL4_TCB_UnsetBreakpoint(debuggee_tcb_cap, TEST_FIRST_INSTR_BP); 78 result = seL4_TCB_ConfigureSingleStepping(debuggee_tcb_cap, TEST_FIRST_INSTR_BP + 1, 1); 79 80 test_eq((int)result.bp_was_consumed, SINGLESTEP_EXPECTED_BP_CONSUMPTION_VALUE); 81 test_eq(result.error, seL4_NoError); 82 83 /* Resume() the debuggee thread, and keep stepping until it reaches the 84 * stop point (when it calls stop_point()). 85 * 86 * Set the counter to 0. The debuggee thread will be incrementing this. 87 * We will check its value afterward to verify that the debuggee ran as 88 * expected. 89 */ 90 counter = 0; 91 seL4_TCB_Resume(debuggee_tcb_cap); 92 93 for (;;) { 94 tag = api_recv(fault_ep_cspath.capPtr, &badge, reply); 95 96 if (seL4_MessageInfo_get_label(tag) != seL4_Fault_DebugException) { 97 ZF_LOGE("Debugger: while single stepping, got unexpected fault.\n"); 98 return -1; 99 } 100 101 fault_data.vaddr = seL4_GetMR(seL4_DebugException_FaultIP); 102 fault_data.reason = seL4_GetMR(seL4_DebugException_ExceptionReason); 103 fault_data.bp_num = seL4_GetMR(seL4_DebugException_TriggerAddress); 104 if (fault_data.reason != seL4_SingleStep) { 105 ZF_LOGE("Debugger: while single stepping, got debug exception, but " 106 "for the wrong reason (reason %zd).\n", 107 fault_data.reason); 108 return -1; 109 } 110 111 if (fault_data.vaddr == (seL4_Word)&stop_point) { 112 /* We're done: disable single-stepping */ 113 ZF_LOGV("About to disable stepping and resume debuggee.\n"); 114 tag = seL4_MessageInfo_set_label(tag, 0); 115 seL4_SetMR(0, 0); 116 api_reply(reply, tag); 117 break; 118 } 119 120 tag = seL4_MessageInfo_set_label(tag, 0); 121 seL4_SetMR(0, 1); 122 api_reply(reply, tag); 123 } 124 125 if (fault_data.vaddr != (seL4_Word)&stop_point) { 126 ZF_LOGE("Exited loop, but the debuggee thread did not get where we " 127 "expected it to.\n"); 128 return -1; 129 } 130 131 /* Test the value of "counter", which the debuggee thread was incrementing. */ 132 if (counter <= 0 || counter != SINGLESTEP_TEST_MAX_LOOP_ITERATIONS) { 133 return -1; 134 } 135 136 return 0; 137} 138 139int debuggee_main(seL4_Word a0, seL4_Word a1, seL4_Word a2, seL4_Word a3) 140{ 141 ZF_LOGV("Debuggee: about to execute guinea_pig.\n"); 142 single_step_guinea_pig(); 143 return 0; 144} 145 146static int test_debug_api_single_step(struct env *env) 147{ 148 helper_thread_t debugger, debuggee; 149 int error; 150 151 test_eq(setup_caps_for_test(env), 0); 152 153 create_helper_thread(env, &debugger); 154 set_helper_priority(env, &debugger, BREAKPOINT_TEST_HANDLER_PRIO); 155 156 error = setup_faulter_thread_for_test(env, &debuggee); 157 test_eq(error, seL4_NoError); 158 error = seL4_TCB_SetBreakpoint(get_helper_tcb(&debuggee), 159 TEST_FIRST_INSTR_BP, (seL4_Word)&single_step_guinea_pig, 160 seL4_InstructionBreakpoint, 0, seL4_BreakOnRead); 161 test_eq(error, seL4_NoError); 162 163 start_helper(env, &debugger, &debugger_main, 164 get_helper_tcb(&debuggee), 165 get_helper_reply(&debuggee), 0, 0); 166 start_helper(env, &debuggee, &debuggee_main, 0, 0, 0, 0); 167 168 wait_for_helper(&debugger); 169 wait_for_helper(&debuggee); 170 171 cleanup_helper(env, &debuggee); 172 cleanup_helper(env, &debugger); 173 return sel4test_get_result(); 174} 175DEFINE_TEST(SINGLESTEP_001, "Attempt to step through a function", 176 test_debug_api_single_step, config_set(CONFIG_HARDWARE_DEBUG_API)); 177 178#endif /* #ifdef CONFIG_HARDWARE_DEBUG_API */ 179