From e5f009a45aca738e2fd78a62641a26ef732b758c Mon Sep 17 00:00:00 2001 From: Alwin Joshy Date: Mon, 15 Jan 2024 17:27:39 +1100 Subject: [PATCH] hardware debug API: aarch64 and single stepping Signed-off-by: Alwin Joshy --- apps/sel4test-tests/arch/arm/arch/debug.h | 22 +++- .../src/arch/arm/tests/breakpoints.c | 109 ++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 apps/sel4test-tests/src/arch/arm/tests/breakpoints.c diff --git a/apps/sel4test-tests/arch/arm/arch/debug.h b/apps/sel4test-tests/arch/arm/arch/debug.h index aec8d537..0f570ede 100644 --- a/apps/sel4test-tests/arch/arm/arch/debug.h +++ b/apps/sel4test-tests/arch/arm/arch/debug.h @@ -5,16 +5,36 @@ */ #pragma once +#ifdef CONFIG_ARCH_AARCH32 #define TEST_SOFTWARE_BREAK_ASM() \ asm volatile( \ ".global sbreak, post_sbreak\n\t" \ ".type post_sbreak, function\n\t" \ "sbreak:\n\t" \ "bkpt\n\t") +#else +#define TEST_SOFTWARE_BREAK_ASM() \ + asm volatile( \ + ".global sbreak, post_sbreak\n\t" \ + ".type post_sbreak, function\n\t" \ + "sbreak:\n\t" \ + "brk #0\n\t") +#endif + +#define TEST_SINGLE_STEP_ASM() \ + asm volatile( \ + ".global label_one, label_two\n\t" \ + "label_one: \n\t" \ + "nop \n\t" \ + "label_two: \n\t" \ + "nop \n\t" \ + ) /* Tell C about the symbols exported by the ASM above. */ -extern char sbreak; +extern char sbreak, label_one, label_two; #define TEST_SOFTWARE_BREAK_EXPECTED_FAULT_LABEL sbreak +#define TEST_SS_LABEL_ONE label_one +#define TEST_SS_LABEL_TWO label_two #define SINGLESTEP_EXPECTED_BP_CONSUMPTION_VALUE (true) #define TEST_NUM_DATA_WPS seL4_NumExclusiveWatchpoints #define TEST_NUM_INSTR_BPS seL4_NumExclusiveBreakpoints diff --git a/apps/sel4test-tests/src/arch/arm/tests/breakpoints.c b/apps/sel4test-tests/src/arch/arm/tests/breakpoints.c new file mode 100644 index 00000000..c49279dc --- /dev/null +++ b/apps/sel4test-tests/src/arch/arm/tests/breakpoints.c @@ -0,0 +1,109 @@ +/* + * Copyright 2024, UNSW Sydney + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#ifdef CONFIG_HARDWARE_DEBUG_API + +#include +#include + +#include +#include "../../../helpers.h" +#include "../../../test.h" +#include "../../../tests/breakpoints.h" + +static int faulter_main(seL4_Word arg0, seL4_Word arg1, seL4_Word arg2, seL4_Word arg3) +{ + TEST_SINGLE_STEP_ASM(); + /* NOTHING BELOW SHOULD EVER BE REACHED */ + int *ptr = NULL; + *ptr = 0xBEEF; + return 0; +} + +static int handler_main(seL4_Word faulter_tcb, seL4_Word a1, seL4_Word a2, seL4_Word a3) +{ + seL4_Word sender_badge; + seL4_MessageInfo_t tag; + seL4_Word label; + + /* Wait for the faulter to trigger an event. */ + tag = api_wait(fault_ep_cspath.capPtr, &sender_badge); + ZF_LOGV("Handler got a fault on the ep.\n"); + label = seL4_MessageInfo_get_label(tag); + + /* Check that it is a breakpoint exception */ + if (label != seL4_Fault_DebugException || + seL4_GetMR(seL4_DebugException_ExceptionReason) != seL4_InstructionBreakpoint) { + ZF_LOGV("Received exception other than instruction breakpoint exception.\n"); + return -1; + } + + /* Disable the breakpoint and enable single stepping */ + int error = seL4_TCB_UnsetBreakpoint(faulter_tcb, 0); + test_eq(error, 0); + + seL4_TCB_ConfigureSingleStepping_t res = seL4_TCB_ConfigureSingleStepping(faulter_tcb, 0, 1); + test_eq(res.error, 0); + + /* Reply to the thread to resume it */ + tag = api_reply_recv(fault_ep_cspath.capPtr, seL4_MessageInfo_new(0, 0, 0, 0), &sender_badge, 0); + label = seL4_MessageInfo_get_label(tag); + + /* Wait for the single step exception */ + fault_data.vaddr = seL4_GetMR(seL4_DebugException_FaultIP); + fault_data.reason = seL4_GetMR(seL4_DebugException_ExceptionReason); + fault_data.vaddr2 = seL4_GetMR(seL4_DebugException_TriggerAddress); + fault_data.bp_num = seL4_GetMR(seL4_DebugException_BreakpointNumber); + + if (label == seL4_Fault_DebugException && + fault_data.reason == seL4_SingleStep && + fault_data.vaddr == (seL4_Word) &TEST_SS_LABEL_TWO) { + + return 0; + } else { + ZF_LOGE("Fault of type %zd received. Vaddr 0x%zx\n", label, fault_data.vaddr); + fault_data.bp_num = 0; + return -1; + } +} + +static int test_single_step_one(struct env *env) +{ + int error, result; + helper_thread_t faulter_thread, handler_thread; + test_eq(setup_caps_for_test(env), 0); + + create_helper_thread(env, &handler_thread); + set_helper_priority(env, &handler_thread, BREAKPOINT_TEST_HANDLER_PRIO); + + error = setup_faulter_thread_for_test(env, &faulter_thread); + test_eq(error, seL4_NoError); + + /* We want it to run until the first label */ + error = seL4_TCB_SetBreakpoint(get_helper_tcb(&faulter_thread), 0, (seL4_Word) &TEST_SS_LABEL_ONE, + seL4_InstructionBreakpoint, 0, seL4_BreakOnRead); + test_eq(error, seL4_NoError); + + start_helper(env, &handler_thread, &handler_main, get_helper_tcb(&faulter_thread), 0, 0, 0); + start_helper(env, &faulter_thread, &faulter_main, 0, 0, 0, 0); + + result = wait_for_helper(&handler_thread); + + cleanup_helper(env, &faulter_thread); + cleanup_helper(env, &handler_thread); + + /* Ensure the test was a success */ + test_eq(result, 0); + + return sel4test_get_result(); +} + +DEFINE_TEST(SINGLESTEP_001, "Test that single stepping 1 instruction works", + test_single_step_one, config_set(CONFIG_HARDWARE_DEBUG_API)) + +#endif /* CONFIG_HARDWARE_DEBUG_API */