[PATCH v1 5/5] cpukit/libdebugger: Add MicroBlaze support

Kinsey Moore kinsey.moore at oarcorp.com
Tue Feb 15 20:38:48 UTC 2022


Add MicroBlaze support for libdebugger. This uses only software break
instructions to provide self-hosted GDB debugging support for
applications since internal control of debug hardware is not possible.
---
 .../libdebugger/rtems-debugger-microblaze.c   | 1440 +++++++++++++++++
 .../cpu/microblaze/include/rtems/score/cpu.h  |   24 +
 spec/build/cpukit/libdebugger.yml             |    2 +
 spec/build/cpukit/objdbgmicroblaze.yml        |   15 +
 spec/build/cpukit/optlibdebugger.yml          |    1 +
 5 files changed, 1482 insertions(+)
 create mode 100644 cpukit/libdebugger/rtems-debugger-microblaze.c
 create mode 100644 spec/build/cpukit/objdbgmicroblaze.yml

diff --git a/cpukit/libdebugger/rtems-debugger-microblaze.c b/cpukit/libdebugger/rtems-debugger-microblaze.c
new file mode 100644
index 0000000000..3e7f9a141d
--- /dev/null
+++ b/cpukit/libdebugger/rtems-debugger-microblaze.c
@@ -0,0 +1,1440 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSLibdebugger
+ *
+ * @brief MicroBlaze libdebugger implementation
+ */
+
+/*
+ * Copyright (C) 2022 On-Line Applications Research Corporation (OAR)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define TARGET_DEBUG 0
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+/* Defined by linkcmds.base */
+extern char bsp_section_text_begin[];
+extern char bsp_section_text_end[];
+extern char bsp_section_fast_text_begin[];
+extern char bsp_section_fast_text_end[];
+
+#include <rtems.h>
+#include <rtems/score/cpu.h>
+#include <rtems/score/threadimpl.h>
+#include <rtems/score/userextimpl.h>
+
+#include <rtems/debugger/rtems-debugger-bsp.h>
+
+#include "rtems-debugger-target.h"
+#include "rtems-debugger-threads.h"
+
+#if TARGET_DEBUG
+#include <rtems/bspIo.h>
+#endif
+
+/*
+ * Number of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGS 57
+
+/*
+ * Number of bytes per type of register.
+ */
+#define RTEMS_DEBUGGER_REG_BYTES    4
+
+/* Debugger registers layout. See microblaze-core.xml in GDB source. */
+#define REG_R0    0
+#define REG_R1    1
+#define REG_R2    2
+#define REG_R3    3
+#define REG_R4    4
+#define REG_R5    5
+#define REG_R6    6
+#define REG_R7    7
+#define REG_R8    8
+#define REG_R9    9
+#define REG_R10   10
+#define REG_R11   11
+#define REG_R12   12
+#define REG_R13   13
+#define REG_R14   14
+#define REG_R15   15
+#define REG_R16   16
+#define REG_R17   17
+#define REG_R18   18
+#define REG_R19   19
+#define REG_R20   20
+#define REG_R21   21
+#define REG_R22   22
+#define REG_R23   23
+#define REG_R24   24
+#define REG_R25   25
+#define REG_R26   26
+#define REG_R27   27
+#define REG_R28   28
+#define REG_R29   29
+#define REG_R30   30
+#define REG_R31   31
+#define REG_PC    32
+#define REG_MS    33
+#define REG_EA    34
+#define REG_ES    35
+#define REG_FS    36
+#define REG_BT    37
+#define REG_PV0   38
+#define REG_PV1   39
+#define REG_PV2   40
+#define REG_PV3   41
+#define REG_PV4   42
+#define REG_PV5   43
+#define REG_PV6   44
+#define REG_PV7   45
+#define REG_PV8   46
+#define REG_PV9   47
+#define REG_PV10  48
+#define REG_PV11  49
+#define REG_ED    50
+#define REG_PID   51
+#define REG_ZP    52
+#define REG_TBLX  53
+#define REG_TBLSX 54
+#define REG_TBLLO 55
+#define REG_TBLHI 56
+
+/**
+ * Register offset table with the total as the last entry.
+ *
+ * Check this table in gdb with the command:
+ *
+ *   maint print registers
+ */
+static const size_t microblaze_reg_offsets[ RTEMS_DEBUGGER_NUMREGS + 1 ] = {
+  REG_R0 * 4,
+  REG_R1 * 4,
+  REG_R2 * 4,
+  REG_R3 * 4,
+  REG_R4 * 4,
+  REG_R5 * 4,
+  REG_R6 * 4,
+  REG_R7 * 4,
+  REG_R8 * 4,
+  REG_R9 * 4,
+  REG_R10 * 4,
+  REG_R11 * 4,
+  REG_R12 * 4,
+  REG_R13 * 4,
+  REG_R14 * 4,
+  REG_R15 * 4,
+  REG_R16 * 4,
+  REG_R17 * 4,
+  REG_R18 * 4,
+  REG_R19 * 4,
+  REG_R20 * 4,
+  REG_R21 * 4,
+  REG_R22 * 4,
+  REG_R23 * 4,
+  REG_R24 * 4,
+  REG_R25 * 4,
+  REG_R26 * 4,
+  REG_R27 * 4,
+  REG_R28 * 4,
+  REG_R29 * 4,
+  REG_R30 * 4,
+  REG_R31 * 4,
+  REG_PC * 4,
+  REG_MS * 4,
+  REG_EA * 4,
+  REG_ES * 4,
+  REG_FS * 4,
+  REG_BT * 4,
+  REG_PV0 * 4,
+  REG_PV1 * 4,
+  REG_PV2 * 4,
+  REG_PV3 * 4,
+  REG_PV4 * 4,
+  REG_PV5 * 4,
+  REG_PV6 * 4,
+  REG_PV7 * 4,
+  REG_PV8 * 4,
+  REG_PV9 * 4,
+  REG_PV10 * 4,
+  REG_PV11 * 4,
+  REG_ED * 4,
+  REG_PID * 4,
+  REG_ZP * 4,
+  REG_TBLX * 4,
+  REG_TBLSX * 4,
+  REG_TBLLO * 4,
+  REG_TBLHI * 4,
+/* Total size */
+  REG_TBLHI * 4 + 4,
+};
+
+/*
+ * Number of bytes of registers.
+ */
+#define RTEMS_DEBUGGER_NUMREGBYTES \
+  microblaze_reg_offsets[ RTEMS_DEBUGGER_NUMREGS ]
+
+/**
+ * Print the exception frame.
+ */
+#define EXC_FRAME_PRINT( _out, _prefix, _frame ) \
+  do { \
+    _out( \
+      _prefix "  R0 = 0x%08" PRIx32 "  R1 = 0x%08" PRIx32 \
+      "  R2 = 0x%08" PRIx32 "  R3 = 0x%08" PRIx32 "\n", \
+      0, \
+      _frame->r1, \
+      _frame->r2, \
+      _frame->r3 \
+    ); \
+    _out( \
+      _prefix "  R4 = 0x%08" PRIx32 "  R5 = 0x%08" PRIx32 \
+      "  R6 = 0x%08" PRIx32 "  R7 = 0x%08" PRIx32 "\n", \
+      _frame->r4, \
+      _frame->r5, \
+      _frame->r6, \
+      _frame->r7 \
+    ); \
+    _out( \
+      _prefix "  R8 = 0x%08" PRIx32 "  R9 = 0x%08" PRIx32 \
+      " R10 = 0x%08" PRIx32 " R11 = 0x%08" PRIx32 "\n", \
+      _frame->r8, \
+      _frame->r9, \
+      _frame->r10, \
+      _frame->r11 \
+    ); \
+    _out( \
+      _prefix " R12 = 0x%08" PRIx32 " R13 = 0x%08" PRIx32 \
+      " R14 = 0x%08" PRIxPTR " R15 = 0x%08" PRIxPTR "\n", \
+      _frame->r12, \
+      _frame->r13, \
+      (uintptr_t) _frame->r14, \
+      (uintptr_t) _frame->r15 \
+    ); \
+    _out( \
+      _prefix " R16 = 0x%08" PRIxPTR " R17 = 0x%08" PRIxPTR \
+      " R18 = 0x%08" PRIx32 " R19 = 0x%08" PRIx32 "\n", \
+      (uintptr_t) _frame->r16, \
+      (uintptr_t) _frame->r17, \
+      _frame->r18, \
+      _frame->r19 \
+    ); \
+    _out( \
+      _prefix " R20 = 0x%08" PRIx32 " R21 = 0x%08" PRIx32 \
+      " R22 = 0x%08" PRIx32 " R23 = 0x%08" PRIx32 "\n", \
+      _frame->r20, \
+      _frame->r21, \
+      _frame->r22, \
+      _frame->r23 \
+    ); \
+    _out( \
+      _prefix " R24 = 0x%08" PRIx32 " R25 = 0x%08" PRIx32 \
+      " R26 = 0x%08" PRIx32 " R27 = 0x%08" PRIx32 "\n", \
+      _frame->r24, \
+      _frame->r25, \
+      _frame->r26, \
+      _frame->r27 \
+    ); \
+    _out( \
+      _prefix " R28 = 0x%08" PRIx32 " R29 = 0x%08" PRIx32 \
+      " R30 = 0x%08" PRIxPTR " R31 = 0x%08" PRIxPTR "\n", \
+      _frame->r28, \
+      _frame->r29, \
+      _frame->r30, \
+      _frame->r31 \
+    ); \
+    _out( \
+      _prefix " EAR = %p ESR = 0x%08" PRIx32 "\n", \
+      _frame->ear, \
+      _frame->esr \
+    ); \
+    _out( \
+      _prefix "  PC = %p\n", \
+      _frame->r16 \
+    ); \
+    _out( \
+      _prefix " MSR = 0x%08" PRIx32 " En:%c%c%c%c Prog:%c%c%c Mode:%c%c Arith:%c%c\n", \
+      _frame->msr, \
+      ( _frame->msr & MICROBLAZE_MSR_IE ) != 0 ? 'I' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_ICE ) != 0 ? 'C' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_DCE ) != 0 ? 'D' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_EE ) != 0 ? 'E' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_BIP ) != 0 ? 'B' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_FSL ) != 0 ? 'F' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_EIP ) != 0 ? 'E' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_UM ) != 0 ? 'U' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_VM ) != 0 ? 'V' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_C ) != 0 ? 'C' : '-', \
+      ( _frame->msr & MICROBLAZE_MSR_DZO ) != 0 ? 'Z' : '-' \
+    ); \
+  } while ( 0 )
+
+/**
+ * The breakpoint instruction: brki r16, 0x18
+ */
+static const uint8_t breakpoint[ 4 ] = { 0x18, 0x00, 0x0c, 0xba };
+
+/**
+ * Target lock.
+ */
+RTEMS_INTERRUPT_LOCK_DEFINE( static, target_lock, "target_lock" )
+
+/**
+ * Is a session active?
+ */
+static bool debug_session_active;
+
+/*
+ * MicroBlaze debug hardware.
+ */
+static uint8_t hw_breakpoints;
+static uint8_t hw_read_watchpoints;
+static uint8_t hw_write_watchpoints;
+
+/* Software breakpoints for single stepping */
+typedef struct {
+  uint32_t *address;
+  bool loaded;
+} microblaze_soft_step;
+
+microblaze_soft_step next_soft_break = { 0, false };
+microblaze_soft_step target_soft_break = { 0, false };
+
+static void set_soft_break(
+  microblaze_soft_step *soft_break,
+  uint32_t             *next_ins
+)
+{
+  soft_break->address = next_ins;
+  rtems_debugger_target_swbreak_control(
+    true,
+    (uintptr_t) soft_break->address,
+    4
+  );
+  soft_break->loaded = true;
+}
+
+static void restore_soft_step( microblaze_soft_step *bp, bool clear_address )
+{
+  /*
+   * Only restore if the breakpoint is active and the instruction at the address
+   * is the breakpoint instruction.
+   */
+  if ( bp->loaded == true && bp->address != NULL ) {
+    rtems_debugger_target_swbreak_control( false, (uintptr_t) bp->address, 4 );
+  }
+
+  bp->loaded = false;
+
+  if ( clear_address == true ) {
+    bp->address = NULL;
+  }
+}
+
+/*
+ * Target debugging support. Use this to debug the backend.
+ */
+#if TARGET_DEBUG
+
+void rtems_debugger_printk_lock( rtems_interrupt_lock_context *lock_context );
+
+void rtems_debugger_printk_unlock(
+  rtems_interrupt_lock_context *lock_context
+);
+
+static void target_printk( const char *format, ... ) RTEMS_PRINTFLIKE( 1, 2 );
+
+static void target_printk( const char *format, ... )
+{
+  rtems_interrupt_lock_context lock_context;
+  va_list                      ap;
+
+  va_start( ap, format );
+  rtems_debugger_printk_lock( &lock_context );
+  vprintk( format, ap );
+  rtems_debugger_printk_unlock( &lock_context );
+  va_end( ap );
+}
+
+#else
+#define target_printk( _fmt, ... )
+#endif
+
+static int microblaze_debug_probe( rtems_debugger_target *target )
+{
+  uint32_t    msr;
+  uint32_t    pvr0;
+  uint32_t    pvr3;
+  const char *version = NULL;
+
+  rtems_debugger_printf(
+    "rtems-db: MicroBlaze\n"
+  );
+
+  _CPU_MSR_GET( msr );
+
+  if ( ( msr & MICROBLAZE_MSR_PVR ) == 0 ) {
+    rtems_debugger_printf(
+      "rtems-db: Processor Version Registers not supported\n"
+    );
+    return 0;
+  }
+
+  _CPU_PVR0_GET( pvr0 );
+
+  switch ( MICROBLAZE_PVR0_VERSION_GET( pvr0 ) ) {
+   case 0x1:
+     version = "v5.00.a";
+     break;
+   case 0x2:
+     version = "v5.00.b";
+     break;
+   case 0x3:
+     version = "v5.00.c";
+     break;
+   case 0x4:
+     version = "v6.00.a";
+     break;
+   case 0x5:
+     version = "v7.00.a";
+     break;
+   case 0x6:
+     version = "v6.00.b";
+     break;
+   case 0x7:
+     version = "v7.00.b";
+     break;
+   case 0x8:
+     version = "v7.10.a";
+     break;
+  }
+
+  rtems_debugger_printf(
+    "rtems-db: Version: %s (%d)\n",
+    version,
+    MICROBLAZE_PVR0_VERSION_GET( pvr0 )
+  );
+
+  /* further PVR supported? */
+  if ( ( pvr0 >> 31 ) == 0 ) {
+    rtems_debugger_printf(
+      "rtems-db: Further Processor Version Registers not supported\n"
+    );
+    return 0;
+  }
+
+  _CPU_PVR3_GET( pvr3 );
+
+  hw_breakpoints = MICROBLAZE_PVR3_BP_GET( pvr3 );
+  hw_read_watchpoints = MICROBLAZE_PVR3_RWP_GET( pvr3 );
+  hw_write_watchpoints = MICROBLAZE_PVR3_WWP_GET( pvr3 );
+
+  rtems_debugger_printf(
+    "rtems-db: breakpoints:%" PRIu32
+    " read watchpoints:%" PRIu32 " write watchpoints:%" PRIu32 "\n",
+    hw_breakpoints,
+    hw_read_watchpoints,
+    hw_write_watchpoints
+  );
+
+  return 0;
+}
+
+int rtems_debugger_target_configure( rtems_debugger_target *target )
+{
+  target->capabilities = ( RTEMS_DEBUGGER_TARGET_CAP_SWBREAK
+    | RTEMS_DEBUGGER_TARGET_CAP_PURE_SWBREAK );
+  target->reg_num = RTEMS_DEBUGGER_NUMREGS;
+  target->reg_offset = microblaze_reg_offsets;
+  target->breakpoint = &breakpoint[ 0 ];
+  target->breakpoint_size = sizeof( breakpoint );
+  return microblaze_debug_probe( target );
+}
+
+static void target_print_frame( CPU_Exception_frame *frame )
+{
+  EXC_FRAME_PRINT( target_printk, "[} ", frame );
+}
+
+/* returns true if cascade is required */
+static bool target_exception( CPU_Exception_frame *frame )
+{
+  target_print_frame( frame );
+
+  switch ( rtems_debugger_target_exception( frame ) ) {
+   case rtems_debugger_target_exc_consumed:
+   default:
+     break;
+   case rtems_debugger_target_exc_step:
+     break;
+   case rtems_debugger_target_exc_cascade:
+     target_printk( "rtems-db: unhandled exception: cascading\n" );
+     /* Continue in fatal error handler chain */
+     return true;
+  }
+
+  target_printk(
+    "[} < resuming frame = %016" PRIxPTR "\n",
+    (uintptr_t) frame
+  );
+  target_print_frame( frame );
+
+  return false;
+}
+
+static void target_exception_handler( CPU_Exception_frame *ef )
+{
+  if ( debug_session_active == false ) {
+    /* Falls into fatal error handler */
+    return;
+  }
+
+  /*
+   * Remove single step software breakpoints since they will need to be
+   * recalculated for the current instruction.
+   */
+  restore_soft_step( &next_soft_break, true );
+  restore_soft_step( &target_soft_break, true );
+
+  /* disable all software breakpoints */
+  rtems_debugger_target_swbreak_remove();
+
+  if ( target_exception( ef ) == true ) {
+    /* Falls into fatal error handler */
+    return;
+  }
+
+  /* Enable all software breakpoints including added single-step breakpoints */
+  rtems_debugger_target_swbreak_insert();
+
+  /* does not return */
+  _CPU_Exception_resume( ef );
+}
+
+static void rtems_debugger_set_int_reg(
+  rtems_debugger_thread *thread,
+  size_t                 reg,
+  const uint32_t         value
+)
+{
+  const size_t offset = microblaze_reg_offsets[ reg ];
+
+  memcpy( &thread->registers[ offset ], &value, sizeof( uint32_t ) );
+}
+
+static const uint32_t rtems_debugger_get_int_reg(
+  rtems_debugger_thread *thread,
+  size_t                 reg
+)
+{
+  const size_t offset = microblaze_reg_offsets[ reg ];
+  uint32_t     value;
+
+  memcpy( &value, &thread->registers[ offset ], sizeof( uint32_t ) );
+  return value;
+}
+
+static void preint_hook( void )
+{
+  Per_CPU_Control *cpu_self = _Per_CPU_Get();
+
+  /* All software breakpoints must be disabled on entry to interrupt context */
+  if ( cpu_self->isr_nest_level != 0 ) {
+    return;
+  }
+
+  /* Handle software breaks */
+  rtems_debugger_target_swbreak_remove();
+}
+
+static bool tid_is_excluded( const rtems_id tid )
+{
+  rtems_debugger_threads *threads = rtems_debugger->threads;
+  rtems_id               *excludes;
+  size_t                  i;
+
+  excludes = rtems_debugger_thread_excludes( threads );
+
+  for ( i = 0; i < threads->excludes.level; ++i ) {
+    if ( tid == excludes[ i ] ) {
+      return true;
+    }
+  }
+
+  /* DBSe is dynamically created and destroyed, so might not actually be in the excludes list */
+  char name[ RTEMS_DEBUGGER_THREAD_NAME_SIZE ];
+
+  rtems_object_get_name( tid, sizeof( name ), (char *) &name[ 0 ] );
+
+  if ( strcmp( "DBSe", name ) == 0 ) {
+    return true;
+  }
+
+  return false;
+}
+
+static void postint_hook( void )
+{
+  Per_CPU_Control *cpu_self = _Per_CPU_Get();
+  Thread_Control  *thread = _Thread_Get_executing();
+  const rtems_id   tid = thread->Object.id;
+
+  /* All software breakpoints must be enabled on exit from interrupt context */
+  if ( cpu_self->isr_nest_level != 0 ) {
+    return;
+  }
+
+  if ( tid_is_excluded( tid ) == true ) {
+    return;
+  }
+
+  /* Enable single-step software breaks */
+  if ( next_soft_break.loaded == false && next_soft_break.address != NULL ) {
+    set_soft_break( &next_soft_break, next_soft_break.address );
+  }
+
+  if ( target_soft_break.loaded == false &&
+       target_soft_break.address != NULL ) {
+    set_soft_break( &target_soft_break, target_soft_break.address );
+  }
+
+  /* Insert all software breaks */
+  rtems_debugger_target_swbreak_insert();
+}
+
+static void mb_thread_switch( Thread_Control *executing, Thread_Control *heir )
+{
+  /*
+   * R13 is meant for TLS usage, but isn't used by RTEMS. It is currently being
+   * coopted as an interrupt context flag. R2 could also serve this purpose, but
+   * it would need to be added to Context_Control.
+   */
+  if ( heir->Registers.r13 != 0 ) {
+    rtems_debugger_target_swbreak_remove();
+    return;
+  }
+
+  if ( tid_is_excluded( heir->Object.id ) == true ) {
+    rtems_debugger_target_swbreak_remove();
+    return;
+  }
+
+  /* Enable single-step software breaks */
+  if ( next_soft_break.loaded == false && next_soft_break.address != NULL ) {
+    set_soft_break( &next_soft_break, next_soft_break.address );
+  }
+
+  if ( target_soft_break.loaded == false &&
+       target_soft_break.address != NULL ) {
+    set_soft_break( &target_soft_break, target_soft_break.address );
+  }
+
+  /* Insert all software breaks */
+  rtems_debugger_target_swbreak_insert();
+}
+
+User_extensions_Control mb_ext = {
+  .Callouts = { .thread_switch = mb_thread_switch }
+};
+
+int rtems_debugger_target_enable( void )
+{
+  debug_session_active = true;
+  rtems_interrupt_lock_context lock_context;
+
+  rtems_interrupt_lock_acquire( &target_lock, &lock_context );
+
+  _MicroBlaze_Debug_install_handler( target_exception_handler, NULL );
+  _MicroBlaze_Exception_install_handler( target_exception_handler, NULL );
+  _MicroBlaze_Pre_Interrupt_install_hook( preint_hook, NULL );
+  _MicroBlaze_Post_Interrupt_install_hook( postint_hook, NULL );
+  _User_extensions_Add_set( &mb_ext );
+
+  rtems_interrupt_lock_release( &target_lock, &lock_context );
+  return RTEMS_SUCCESSFUL;
+}
+
+int rtems_debugger_target_disable( void )
+{
+  debug_session_active = false;
+  rtems_interrupt_lock_context lock_context;
+
+  rtems_interrupt_lock_acquire( &target_lock, &lock_context );
+
+  _User_extensions_Remove_set( &mb_ext );
+
+  rtems_interrupt_lock_release( &target_lock, &lock_context );
+  return RTEMS_SUCCESSFUL;
+}
+
+int rtems_debugger_target_read_regs( rtems_debugger_thread *thread )
+{
+  if (
+    rtems_debugger_thread_flag(
+      thread,
+      RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID
+    ) == 0
+  ) {
+    static const uintptr_t good_address = (uintptr_t) &good_address;
+    int                    i;
+
+    memset( &thread->registers[ 0 ], 0, RTEMS_DEBUGGER_NUMREGBYTES );
+
+    /* set all integer register to a known valid address */
+    for ( i = 0; i < RTEMS_DEBUGGER_NUMREGS; ++i ) {
+      rtems_debugger_set_int_reg( thread, i, (uintptr_t) &good_address );
+    }
+
+    if ( thread->frame != NULL ) {
+      CPU_Exception_frame *frame = thread->frame;
+
+      *( (CPU_Exception_frame *) thread->registers ) = *frame;
+      rtems_debugger_set_int_reg( thread, REG_R0, 0 );
+      rtems_debugger_set_int_reg( thread, REG_R1, frame->r1 );
+      rtems_debugger_set_int_reg( thread, REG_R2, frame->r2 );
+      rtems_debugger_set_int_reg( thread, REG_R3, frame->r3 );
+      rtems_debugger_set_int_reg( thread, REG_R4, frame->r4 );
+      rtems_debugger_set_int_reg( thread, REG_R5, frame->r5 );
+      rtems_debugger_set_int_reg( thread, REG_R6, frame->r6 );
+      rtems_debugger_set_int_reg( thread, REG_R7, frame->r7 );
+      rtems_debugger_set_int_reg( thread, REG_R8, frame->r8 );
+      rtems_debugger_set_int_reg( thread, REG_R9, frame->r9 );
+      rtems_debugger_set_int_reg( thread, REG_R10, frame->r10 );
+      rtems_debugger_set_int_reg( thread, REG_R11, frame->r11 );
+      rtems_debugger_set_int_reg( thread, REG_R12, frame->r12 );
+      rtems_debugger_set_int_reg( thread, REG_R13, frame->r13 );
+      rtems_debugger_set_int_reg( thread, REG_R14, (uintptr_t) frame->r14 );
+      rtems_debugger_set_int_reg( thread, REG_R15, (uintptr_t) frame->r15 );
+      rtems_debugger_set_int_reg( thread, REG_R16, (uintptr_t) frame->r16 );
+      rtems_debugger_set_int_reg( thread, REG_R17, (uintptr_t) frame->r17 );
+      rtems_debugger_set_int_reg( thread, REG_R18, frame->r18 );
+      rtems_debugger_set_int_reg( thread, REG_R19, frame->r19 );
+      rtems_debugger_set_int_reg( thread, REG_R20, frame->r20 );
+      rtems_debugger_set_int_reg( thread, REG_R21, frame->r21 );
+      rtems_debugger_set_int_reg( thread, REG_R22, frame->r22 );
+      rtems_debugger_set_int_reg( thread, REG_R23, frame->r23 );
+      rtems_debugger_set_int_reg( thread, REG_R24, frame->r24 );
+      rtems_debugger_set_int_reg( thread, REG_R25, frame->r25 );
+      rtems_debugger_set_int_reg( thread, REG_R26, frame->r26 );
+      rtems_debugger_set_int_reg( thread, REG_R27, frame->r27 );
+      rtems_debugger_set_int_reg( thread, REG_R28, frame->r28 );
+      rtems_debugger_set_int_reg( thread, REG_R29, frame->r29 );
+      rtems_debugger_set_int_reg( thread, REG_R30, frame->r30 );
+      rtems_debugger_set_int_reg( thread, REG_R31, frame->r31 );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_PC,
+        rtems_debugger_target_frame_pc( frame )
+      );
+      rtems_debugger_set_int_reg( thread, REG_MS, frame->msr );
+      rtems_debugger_set_int_reg( thread, REG_EA, (uintptr_t) frame->ear );
+      rtems_debugger_set_int_reg( thread, REG_ES, frame->esr );
+      rtems_debugger_set_int_reg( thread, REG_BT, (uintptr_t) frame->btr );
+      /*
+       * Get the signal from the frame.
+       */
+      thread->signal = rtems_debugger_target_exception_to_signal( frame );
+    } else {
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R1,
+        thread->tcb->Registers.r1
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R13,
+        thread->tcb->Registers.r13
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R14,
+        thread->tcb->Registers.r14
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R15,
+        thread->tcb->Registers.r15
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R16,
+        thread->tcb->Registers.r16
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R17,
+        thread->tcb->Registers.r17
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R18,
+        thread->tcb->Registers.r18
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R19,
+        thread->tcb->Registers.r19
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R20,
+        thread->tcb->Registers.r20
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R21,
+        thread->tcb->Registers.r21
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R22,
+        thread->tcb->Registers.r22
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R23,
+        thread->tcb->Registers.r23
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R24,
+        thread->tcb->Registers.r24
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R25,
+        thread->tcb->Registers.r25
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R26,
+        thread->tcb->Registers.r26
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R27,
+        thread->tcb->Registers.r27
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R28,
+        thread->tcb->Registers.r28
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R29,
+        thread->tcb->Registers.r29
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R30,
+        thread->tcb->Registers.r30
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_R31,
+        thread->tcb->Registers.r31
+      );
+      rtems_debugger_set_int_reg(
+        thread,
+        REG_MS,
+        (intptr_t) thread->tcb->Registers.rmsr
+      );
+      /*
+       * Blocked threads have no signal.
+       */
+      thread->signal = 0;
+    }
+
+    thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID;
+    thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+  }
+
+  return 0;
+}
+
+int rtems_debugger_target_write_regs( rtems_debugger_thread *thread )
+{
+  if (
+    rtems_debugger_thread_flag(
+      thread,
+      RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY
+    ) != 0
+  ) {
+    /*
+     * Only write to debugger controlled exception threads. Do not touch the
+     * registers for threads blocked in the context switcher.
+     */
+    if (
+      rtems_debugger_thread_flag(
+        thread,
+        RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION
+      ) != 0
+    ) {
+      CPU_Exception_frame *frame = thread->frame;
+      frame->r1 = rtems_debugger_get_int_reg( thread, REG_R1 );
+      frame->r2 = rtems_debugger_get_int_reg( thread, REG_R2 );
+      frame->r3 = rtems_debugger_get_int_reg( thread, REG_R3 );
+      frame->r4 = rtems_debugger_get_int_reg( thread, REG_R4 );
+      frame->r5 = rtems_debugger_get_int_reg( thread, REG_R5 );
+      frame->r6 = rtems_debugger_get_int_reg( thread, REG_R6 );
+      frame->r7 = rtems_debugger_get_int_reg( thread, REG_R7 );
+      frame->r8 = rtems_debugger_get_int_reg( thread, REG_R8 );
+      frame->r9 = rtems_debugger_get_int_reg( thread, REG_R9 );
+      frame->r10 = rtems_debugger_get_int_reg( thread, REG_R10 );
+      frame->r11 = rtems_debugger_get_int_reg( thread, REG_R11 );
+      frame->r12 = rtems_debugger_get_int_reg( thread, REG_R12 );
+      frame->r13 = rtems_debugger_get_int_reg( thread, REG_R13 );
+      frame->r14 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R14 );
+      frame->r15 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R15 );
+      frame->r16 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R16 );
+      frame->r17 = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_R17 );
+      frame->r18 = rtems_debugger_get_int_reg( thread, REG_R18 );
+      frame->r19 = rtems_debugger_get_int_reg( thread, REG_R19 );
+      frame->r20 = rtems_debugger_get_int_reg( thread, REG_R20 );
+      frame->r21 = rtems_debugger_get_int_reg( thread, REG_R21 );
+      frame->r22 = rtems_debugger_get_int_reg( thread, REG_R22 );
+      frame->r23 = rtems_debugger_get_int_reg( thread, REG_R23 );
+      frame->r24 = rtems_debugger_get_int_reg( thread, REG_R24 );
+      frame->r25 = rtems_debugger_get_int_reg( thread, REG_R25 );
+      frame->r26 = rtems_debugger_get_int_reg( thread, REG_R26 );
+      frame->r27 = rtems_debugger_get_int_reg( thread, REG_R27 );
+      frame->r28 = rtems_debugger_get_int_reg( thread, REG_R28 );
+      frame->r29 = rtems_debugger_get_int_reg( thread, REG_R29 );
+      frame->r30 = rtems_debugger_get_int_reg( thread, REG_R30 );
+      frame->r31 = rtems_debugger_get_int_reg( thread, REG_R31 );
+      frame->msr = rtems_debugger_get_int_reg( thread, REG_MS );
+      frame->ear = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_EA );
+      frame->esr = rtems_debugger_get_int_reg( thread, REG_ES );
+      frame->btr = (uint32_t *) rtems_debugger_get_int_reg( thread, REG_BT );
+    }
+
+    thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
+  }
+
+  return 0;
+}
+
+uintptr_t rtems_debugger_target_reg_pc( rtems_debugger_thread *thread )
+{
+  return thread->tcb->Registers.r15;
+}
+
+uintptr_t rtems_debugger_target_frame_pc( CPU_Exception_frame *frame )
+{
+  return (uintptr_t) _MicroBlaze_Get_return_address( frame );
+}
+
+uintptr_t rtems_debugger_target_reg_sp( rtems_debugger_thread *thread )
+{
+  int r;
+
+  r = rtems_debugger_target_read_regs( thread );
+
+  if ( r >= 0 ) {
+    return rtems_debugger_get_int_reg( thread, REG_R1 );
+  }
+
+  return 0;
+}
+
+uintptr_t rtems_debugger_target_tcb_sp( rtems_debugger_thread *thread )
+{
+  return (uintptr_t) thread->tcb->Registers.r1;
+}
+
+#define IGROUP_MASK 0x3f
+
+static uint32_t get_igroup( uint32_t ins )
+{
+  return ( ins >> 26 ) & IGROUP_MASK;
+}
+
+#define REGISTER_MASK 0x1f
+
+static uint32_t get_Ra( uint32_t ins )
+{
+  return ( ins >> 16 ) & REGISTER_MASK;
+}
+
+static uint32_t get_Rb( uint32_t ins )
+{
+  return ( ins >> 11 ) & REGISTER_MASK;
+}
+
+static uint32_t get_Rd( uint32_t ins )
+{
+  return ( ins >> 21 ) & REGISTER_MASK;
+}
+
+#define IMM16_MASK 0xffff
+
+static int32_t get_Imm16( uint32_t ins )
+{
+  int16_t base = (int16_t) ins & IMM16_MASK;
+
+  return base;
+}
+
+#define IMM24_MASK 0xffffff
+
+static int32_t get_Imm24( uint32_t ins )
+{
+  int32_t base = ins & IMM24_MASK;
+
+  /* Sign-extend manually if necessary */
+  if ( ( base & 0x800000 ) != 0 ) {
+    base &= 0xFF000000;
+  }
+
+  return base;
+}
+
+static int64_t get_Imm( uint32_t ins )
+{
+  if ( ( get_Rd( ins ) & 0x10 ) != 0 ) {
+    return get_Imm24( ins );
+  }
+
+  return get_Imm16( ins );
+}
+
+#define IMM_GROUP 0x2c
+
+static bool is_imm( uint32_t ins )
+{
+  return get_igroup( ins ) == IMM_GROUP;
+}
+
+#define RETURN_GROUP 0x2d
+
+static bool is_return( uint32_t ins )
+{
+  return get_igroup( ins ) == RETURN_GROUP;
+}
+
+/* Unconditional branch */
+#define UBRANCH_GROUP 0x26
+
+static bool is_ubranch( uint32_t ins )
+{
+  return get_igroup( ins ) == UBRANCH_GROUP;
+}
+
+/* Comparison branch */
+#define CBRANCH_GROUP 0x27
+
+static bool is_cbranch( uint32_t ins )
+{
+  return get_igroup( ins ) == CBRANCH_GROUP;
+}
+
+/* Unconditional Immediate branch */
+#define UIBRANCH_GROUP 0x2e
+
+static bool is_uibranch( uint32_t ins )
+{
+  /* Ra == 0x2 is a memory barrier which continues at the next instruction */
+  return get_igroup( ins ) == UIBRANCH_GROUP && get_Ra( ins ) != 0x2;
+}
+
+/* Comparison Immediate branch */
+#define CIBRANCH_GROUP 0x2f
+
+static bool is_cibranch( uint32_t ins )
+{
+  return get_igroup( ins ) == CIBRANCH_GROUP;
+}
+
+static bool branch_has_delay_slot( uint32_t ins )
+{
+  if ( is_ubranch( ins ) == true && ( get_Ra( ins ) & 0x10 ) != 0 ) {
+    return true;
+  }
+
+  if ( is_cbranch( ins ) == true && ( get_Ra( ins ) & 0x10 ) != 0 ) {
+    return true;
+  }
+
+  if ( is_uibranch( ins ) == true && ( get_Ra( ins ) & 0x10 ) != 0 ) {
+    return true;
+  }
+
+  if ( is_cibranch( ins ) == true && ( get_Rd( ins ) & 0x10 ) != 0 ) {
+    return true;
+  }
+
+  return false;
+}
+
+/* All return instructions have a delay slot */
+
+static bool branch_is_absolute( uint32_t ins )
+{
+  return ( is_ubranch( ins ) == true || is_uibranch( ins ) == true ) &&
+         ( get_Ra( ins ) & 0x8 ) != 0;
+}
+
+/* All returns are absolute */
+
+static bool target_is_absolute( uint32_t ins )
+{
+  return branch_is_absolute( ins ) == true || is_return( ins ) == true;
+}
+
+static bool is_branch( uint32_t ins )
+{
+  if ( is_ubranch( ins ) == true ) {
+    return true;
+  }
+
+  if ( is_cbranch( ins ) == true ) {
+    return true;
+  }
+
+  if ( is_uibranch( ins ) == true ) {
+    return true;
+  }
+
+  if ( is_cibranch( ins ) == true ) {
+    return true;
+  }
+
+  return false;
+}
+
+#define BRK_RA 0xC
+
+static bool is_brk( uint32_t ins )
+{
+  return ( is_ubranch( ins ) == true || is_uibranch( ins ) == true ) &&
+         get_Ra( ins ) == BRK_RA;
+}
+
+static uint32_t get_register_value(
+  CPU_Exception_frame *frame,
+  uint32_t             target_register
+)
+{
+  if ( target_register == 0 ) {
+    return 0;
+  }
+
+  /* Assumes all registers are contiguous and accounted for */
+  return ( &( frame->r1 ) )[ target_register - 1 ];
+}
+
+static void set_frame_pc( CPU_Exception_frame *frame, uint32_t *new_pc )
+{
+  Per_CPU_Control *cpu_self = _Per_CPU_Get();
+
+  /* Break in progress */
+  if ( ( frame->msr & MICROBLAZE_MSR_BIP ) != 0 ) {
+    frame->r16 = (uint32_t *) new_pc;
+    return;
+  }
+
+  /* Exception in progress */
+  if ( ( frame->msr & MICROBLAZE_MSR_EIP ) != 0 ) {
+    frame->r17 = (uint32_t *) new_pc;
+    return;
+  }
+
+  /* Interrupt in progress must be determined by stack pointer location */
+  if (
+    frame->r1 >= (uint32_t) cpu_self->interrupt_stack_low
+    && frame->r1 < (uint32_t) cpu_self->interrupt_stack_high
+  ) {
+    frame->r14 = (uint32_t *) new_pc;
+    return;
+  }
+
+  /* Default to normal link register */
+  frame->r15 = (uint32_t *) new_pc;
+}
+
+static uint32_t bypass_swbreaks( uint32_t *addr )
+{
+  rtems_debugger_target *target = rtems_debugger->target;
+
+  if ( target != NULL && target->swbreaks.block != NULL ) {
+    rtems_debugger_target_swbreak *swbreaks = target->swbreaks.block;
+    size_t                         i;
+
+    for ( i = 0; i < target->swbreaks.level; ++i ) {
+      if ( swbreaks[ i ].address == addr ) {
+        return *( (uint32_t *) &( swbreaks[ i ].contents[ 0 ] ) );
+      }
+    }
+  }
+
+  return *addr;
+}
+
+static int setup_single_step_breakpoints( CPU_Exception_frame *frame )
+{
+  /*
+   * It may be necessary to evaluate the current instruction and next immediate
+   * instruction to determine the address of the "next" instruction and possible
+   * branch target instructions
+   */
+  uint32_t *pc = (uint32_t *) rtems_debugger_target_frame_pc( frame );
+  int64_t   imm = 0;
+  uint32_t *resume_pc;
+
+  /*
+   * Normalize PC address to the real instruction and not any IMM. This deals
+   * with a possible cascade of IMM.
+   */
+  while ( is_imm( bypass_swbreaks( pc ) ) == true ) {
+    pc = &pc[ 1 ];
+  }
+
+  resume_pc = pc;
+
+  /*
+   * If execution ends up on a branch instruction that is preceeded by IMM, bad
+   * things can happen since it's not possible to know if the IMM was actually
+   * executed or something jumped to the branch directly. Exceptions treat IMM
+   * as part of the following instruction, so the RTEMS debugger will do so as
+   * well.
+   */
+  uint32_t bypass_ins = bypass_swbreaks( &pc[ -1 ] );
+
+  if ( is_imm( bypass_ins ) == true ) {
+    imm = get_Imm( bypass_ins );
+    imm <<= 16;
+    resume_pc = &pc[ -1 ];
+  }
+
+  uint32_t ins = bypass_swbreaks( pc );
+  bool     needs_target_break = false;
+  bool     needs_next_break = true;
+
+  if ( is_brk( ins ) == true ) {
+    /*
+     * If the instruction being stepped is brk or brki, something bad has
+     * happened. If this instruction is stepped, the target of the branch (the
+     * debug vector) has a brki placed in it which results in an tight infinite
+     * recursive call. Under normal circumstances, this shouldn't happen.
+     */
+    rtems_debugger_printf(
+      "rtems-db: Unable to set single-step breakpoints for brk/brki instructions\n"
+    );
+
+    return -1;
+  }
+
+  if ( is_branch( ins ) == true ) {
+    needs_target_break = true;
+
+    /*
+     * Unconditional branches (including returns) do not need to break on the
+     * next instruction.
+     */
+    if (
+      is_ubranch( ins ) == true
+      || is_uibranch( ins ) == true
+      || is_return( ins ) == true
+    ) {
+      needs_next_break = false;
+    }
+  }
+
+  if ( is_return( ins ) == true ) {
+    needs_target_break = true;
+    needs_next_break = false;
+  }
+
+  if ( needs_next_break == true ) {
+    uint32_t *next_ins = &pc[ 1 ];
+
+    if ( branch_has_delay_slot( ins ) == true ) {
+      next_ins = &pc[ 2 ];
+    }
+
+    if ( is_brk( *next_ins ) == false ) {
+      /* setup next instruction software break */
+      set_soft_break( &next_soft_break, next_ins );
+    }
+  }
+
+  imm += get_Imm16( ins );
+
+  if ( needs_target_break == true ) {
+    /* Calculate target address */
+    uintptr_t target_ins = 0;
+
+    if ( target_is_absolute( ins ) == false ) {
+      target_ins += (uintptr_t) pc;
+    }
+
+    if (
+      is_uibranch( ins ) == true || is_cibranch( ins ) == true ||
+      is_return( ins ) == true
+    ) {
+      target_ins += imm;
+    }
+
+    if ( is_return( ins ) == true ) {
+      uint32_t target_register = get_Ra( ins );
+      target_ins += get_register_value( frame, target_register );
+    }
+
+    if ( is_ubranch( ins ) == true || is_cbranch( ins ) == true ) {
+      uint32_t target_register = get_Rb( ins );
+      target_ins += get_register_value( frame, target_register );
+    }
+
+    if ( is_brk( *( (uint32_t *) target_ins ) ) == false ) {
+      /* setup target instruction software break */
+      set_soft_break( &target_soft_break, (uint32_t *) target_ins );
+    }
+  }
+
+  /* Alter resume address */
+  set_frame_pc( frame, resume_pc );
+
+  return 0;
+}
+
+int rtems_debugger_target_thread_stepping( rtems_debugger_thread *thread )
+{
+  CPU_Exception_frame *frame = thread->frame;
+  int ret = 0;
+
+  if (
+    rtems_debugger_thread_flag(
+      thread,
+      RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR
+    ) != 0
+  ) {
+    /* Especially on first startup, frame isn't guaranteed to be non-NULL */
+    if ( frame == NULL ) {
+      return -1;
+    }
+
+    /* set software breakpoint(s) here */
+    ret = setup_single_step_breakpoints( frame );
+  }
+
+  return ret;
+}
+
+int rtems_debugger_target_exception_to_signal( CPU_Exception_frame *frame )
+{
+  uint32_t BiP = frame->msr & MICROBLAZE_MSR_BIP;
+  uint32_t EiP = frame->msr & MICROBLAZE_MSR_EIP;
+
+  if ( BiP != 0 ) {
+    return RTEMS_DEBUGGER_SIGNAL_TRAP;
+  }
+
+  if ( EiP != 0 ) {
+    uint32_t EC = frame->esr & 0x1f;
+
+    switch ( EC ) {
+     case 0x0: /* FSL */
+     case 0x1: /* Unaligned data access */
+     case 0x3: /* instruction fetch */
+     case 0x4: /* data bus error */
+     case 0x10: /* MMU data storage */
+     case 0x11: /* MMU instruction storage */
+     case 0x12: /* MMU data TLB miss */
+     case 0x13: /* MMU instruction TLB miss */
+       return RTEMS_DEBUGGER_SIGNAL_SEGV;
+
+     case 0x7: /* priveleged */
+       return RTEMS_DEBUGGER_SIGNAL_TRAP;
+
+     case 0x5: /* div/0 */
+     case 0x6: /* FPU */
+       return RTEMS_DEBUGGER_SIGNAL_FPE;
+
+     case 0x2: /* illegal opcode (unknown instruction) */
+     default:
+       return RTEMS_DEBUGGER_SIGNAL_ILL;
+    }
+  }
+
+  /* Default to SIGILL */
+  return RTEMS_DEBUGGER_SIGNAL_ILL;
+}
+
+void rtems_debugger_target_exception_print( CPU_Exception_frame *frame )
+{
+  EXC_FRAME_PRINT( rtems_debugger_printf, "", frame );
+}
+
+/*
+ * Debug hardware is inaccessible to the CPU, so hardware breaks and watchpoints
+ * are not supported.
+ */
+int rtems_debugger_target_hwbreak_insert( void )
+{
+  return 0;
+}
+
+int rtems_debugger_target_hwbreak_remove( void )
+{
+  return 0;
+}
+
+int rtems_debugger_target_hwbreak_control(
+  rtems_debugger_target_watchpoint wp,
+  bool                             insert,
+  uintptr_t                        addr,
+  DB_UINT                          kind
+)
+{
+  return 0;
+}
+
+int rtems_debugger_target_cache_sync( rtems_debugger_target_swbreak *swbreak )
+{
+  /*
+   * Flush the data cache and invalidate the instruction cache.
+   */
+  rtems_cache_flush_multiple_data_lines(
+    swbreak->address,
+    sizeof( breakpoint )
+  );
+  rtems_cache_instruction_sync_after_code_change(
+    swbreak->address,
+    sizeof( breakpoint )
+  );
+  return 0;
+}
diff --git a/cpukit/score/cpu/microblaze/include/rtems/score/cpu.h b/cpukit/score/cpu/microblaze/include/rtems/score/cpu.h
index 9c6b213e20..63f64ada2a 100644
--- a/cpukit/score/cpu/microblaze/include/rtems/score/cpu.h
+++ b/cpukit/score/cpu/microblaze/include/rtems/score/cpu.h
@@ -208,6 +208,30 @@ typedef struct {
 #define _CPU_MSR_SET( _msr_value ) \
 { __asm__ volatile ("mts rmsr, %0" : "=&r" ((_msr_value)) : "0" ((_msr_value))); }
 
+#define MICROBLAZE_PVR0_VERSION_GET( _pvr0_value ) \
+  ( ( _pvr0_value >> 8 ) & 0xff )
+
+#define _CPU_PVR0_GET( _pvr0_value ) \
+  do { \
+    ( _pvr0_value ) = 0; \
+    __asm__ volatile ( "mfs %0, rpvr0" : "=&r" ( ( _pvr0_value ) ) ); \
+  } while ( 0 )
+
+#define MICROBLAZE_PVR3_BP_GET( _pvr3_value ) \
+  ( ( _pvr3_value >> 25 ) & 0xf )
+
+#define MICROBLAZE_PVR3_RWP_GET( _pvr3_value ) \
+  ( ( _pvr3_value >> 19 ) & 0x7 )
+
+#define MICROBLAZE_PVR3_WWP_GET( _pvr3_value ) \
+  ( ( _pvr3_value >> 13 ) & 0x7 )
+
+#define _CPU_PVR3_GET( _pvr3_value ) \
+  do { \
+    ( _pvr3_value ) = 0; \
+    __asm__ volatile ( "mfs %0, rpvr3" : "=&r" ( ( _pvr3_value ) ) ); \
+  } while ( 0 )
+
 #define _CPU_ISR_Disable( _isr_cookie ) \
   { \
     unsigned int _new_msr;  \
diff --git a/spec/build/cpukit/libdebugger.yml b/spec/build/cpukit/libdebugger.yml
index 9b50adbe77..263a4994f0 100644
--- a/spec/build/cpukit/libdebugger.yml
+++ b/spec/build/cpukit/libdebugger.yml
@@ -16,6 +16,8 @@ links:
   uid: objdbgarm
 - role: build-dependency
   uid: objdbgi386
+- role: build-dependency
+  uid: objdbgmicroblaze
 source:
 - cpukit/libdebugger/rtems-debugger-block.c
 - cpukit/libdebugger/rtems-debugger-bsp.c
diff --git a/spec/build/cpukit/objdbgmicroblaze.yml b/spec/build/cpukit/objdbgmicroblaze.yml
new file mode 100644
index 0000000000..59219ae507
--- /dev/null
+++ b/spec/build/cpukit/objdbgmicroblaze.yml
@@ -0,0 +1,15 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+build-type: objects
+cflags: []
+copyrights:
+- Copyright (C) 2022 On-Line Applications Research (OAR)
+cppflags: []
+cxxflags: []
+enabled-by:
+- microblaze
+includes: []
+install: []
+links: []
+source:
+- cpukit/libdebugger/rtems-debugger-microblaze.c
+type: build
diff --git a/spec/build/cpukit/optlibdebugger.yml b/spec/build/cpukit/optlibdebugger.yml
index a89a495528..c657db4ba4 100644
--- a/spec/build/cpukit/optlibdebugger.yml
+++ b/spec/build/cpukit/optlibdebugger.yml
@@ -12,6 +12,7 @@ enabled-by:
 - aarch64
 - arm
 - i386
+- microblaze
 links: []
 name: BUILD_LIBDEBUGGER
 type: build
-- 
2.30.2



More information about the devel mailing list