Patchwork [4/6] target-mips: add MDI semihosting support to mipssim machine

login
register
mail settings
Submitter Nathan Froyd
Date Aug. 3, 2009, 2:45 p.m.
Message ID <1249310711-8873-5-git-send-email-froydnj@codesourcery.com>
Download mbox | patch
Permalink /patch/30647/
State Superseded
Headers show

Comments

Nathan Froyd - Aug. 3, 2009, 2:45 p.m.
We need to grovel through the .sdeosabi section to find out where
_mdi_syscall is located.  Once we find it, we can set a breakpoint
there.

We use BP_CPU breakpoints to implement the semihosting breakpoint.
Writing BREAK instructions means that users could unintentionally remove
breakpoints, either by means of buggy programs, or mistyped commands.
Using BP_CPU breakpoints means that the user cannot access them and
therefore that they will not be removed (although BP_CPU breakpoints can
be overridden by BP_GDB breakpoints; we assume that the user knows what
he or she is doing if a breakpoint is set at _mdi_syscall).

The shuffling of #includes in helper.c is to deal with peculiarities of
dyngen-exec.h.

Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
 hw/mips_mipssim.c    |   58 +++++++++++++++++++++++++++++++++++++++++++++++--
 target-mips/cpu.h    |    2 +
 target-mips/helper.c |   50 +++++++++++++++++++++++++++++++++++++++---
 3 files changed, 103 insertions(+), 7 deletions(-)

Patch

diff --git a/hw/mips_mipssim.c b/hw/mips_mipssim.c
index 6080dc8..7da3269 100644
--- a/hw/mips_mipssim.c
+++ b/hw/mips_mipssim.c
@@ -48,6 +48,50 @@  static struct _loaderparams {
     const char *initrd_filename;
 } loaderparams;
 
+static uint32_t mdi_semihost_bkpt;
+
+static void find_sdeosabi_section (int fd, int must_swab,
+                                   uint64_t size, uint64_t offset,
+                                   char *name)
+{
+/* We don't support semihosting for 64-bit targets */
+#ifndef TARGET_MIPS64
+    if (semihosting_enabled &&
+        size >= 8 &&
+        strcmp (name, ".sdeosabi") == 0) {
+        uint64_t section_offset = 0;
+
+        if (lseek(fd, offset, SEEK_SET) < 0)
+            return;
+
+        while (section_offset < size) {
+            /* .sdeosabi is organized into pairs of 4-byte words.  The
+               first word in each pair is a numeric tag; the second word
+               is interpreted according to the tag.  For our purposes,
+               we're looking for tag 2.  The second word will be the
+               address of the _mdi_syscall function.  */
+            uint32_t bkpt_info[2];
+
+            if (read(fd, bkpt_info, sizeof(bkpt_info)) == sizeof(bkpt_info)) {
+                if (must_swab) {
+                    bswap32s (&bkpt_info[0]);
+                    bswap32s (&bkpt_info[1]);
+                }
+
+                if (bkpt_info[0] == 2 && bkpt_info[1]) {
+                    mdi_semihost_bkpt = bkpt_info[1];
+                    break;
+                }
+            } else {
+                break;
+            }
+
+            section_offset += sizeof(bkpt_info);
+        }
+    }
+#endif
+}
+
 static void load_kernel (CPUState *env)
 {
     int64_t entry, kernel_low, kernel_high;
@@ -55,9 +99,12 @@  static void load_kernel (CPUState *env)
     long initrd_size;
     ram_addr_t initrd_offset;
 
-    kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND,
-                           (uint64_t *)&entry, (uint64_t *)&kernel_low,
-                           (uint64_t *)&kernel_high);
+    kernel_size = load_elf_introspect(loaderparams.kernel_filename,
+                                      VIRT_TO_PHYS_ADDEND,
+                                      (uint64_t *)&entry,
+                                      (uint64_t *)&kernel_low,
+                                      (uint64_t *)&kernel_high,
+                                      find_sdeosabi_section);
     if (kernel_size >= 0) {
         if ((entry & ~0x7fffffffULL) == 0x80000000)
             entry = (int32_t)entry;
@@ -68,6 +115,11 @@  static void load_kernel (CPUState *env)
         exit(1);
     }
 
+    /* set up semihosting */
+    if (semihosting_enabled && mdi_semihost_bkpt) {
+        install_semihosting_breakpoint(env, mdi_semihost_bkpt);
+    }
+
     /* load initrd */
     initrd_size = 0;
     initrd_offset = 0;
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 1510244..eb495fd 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -588,6 +588,8 @@  void do_interrupt (CPUState *env);
 void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra);
 
 void do_mips_semihosting(CPUState *env);
+void install_semihosting_breakpoint(CPUState *env, uint32_t bkpt_address);
+
 static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
 {
     env->active_tc.PC = tb->pc;
diff --git a/target-mips/helper.c b/target-mips/helper.c
index 7369025..db9ee8d 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -18,13 +18,10 @@ 
  */
 #include <stdarg.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
-#include <inttypes.h>
 #include <signal.h>
 
-#include "cpu.h"
-#include "exec-all.h"
+#include "exec.h"
 
 enum {
     TLBRET_DIRTY = -4,
@@ -340,6 +337,51 @@  static const char * const excp_names[EXCP_LAST + 1] = {
     [EXCP_CACHE] = "cache error",
 };
 
+#if !defined(CONFIG_USER_ONLY)
+extern int semihosting_enabled;
+static uint32_t mdi_semihost_breakpoint;
+
+static CPUDebugExcpHandler *prev_debug_excp_handler;
+static CPUBreakpoint *semihosting_breakpoint;
+
+static void breakpoint_handler(CPUState *env)
+{
+    CPUBreakpoint *bp;
+    int semihosting_done = 0;
+
+    TAILQ_FOREACH(bp, &env->breakpoints, entry) {
+        if (bp->pc == env->active_tc.PC &&
+            semihosting_enabled &&
+            mdi_semihost_breakpoint &&
+            bp->pc == mdi_semihost_breakpoint &&
+            bp->flags & BP_CPU) {
+            do_mips_semihosting(env);
+            semihosting_done = 1;
+            break;
+        }
+    }
+    if (prev_debug_excp_handler) {
+        prev_debug_excp_handler(env);
+    }
+    if (semihosting_done) {
+        /* Reset exception state and return.  */
+        env->exception_index = -1;
+        cpu_loop_exit();
+    }
+}
+
+void install_semihosting_breakpoint(CPUState *env, uint32_t bkpt_address)
+{
+    if (!semihosting_breakpoint && semihosting_enabled) {
+        mdi_semihost_breakpoint = bkpt_address & ~(uint32_t)1;
+        cpu_breakpoint_insert(env, mdi_semihost_breakpoint, BP_CPU,
+                              &semihosting_breakpoint);
+        prev_debug_excp_handler =
+            cpu_set_debug_excp_handler(breakpoint_handler);
+    }
+}
+#endif
+
 void do_interrupt (CPUState *env)
 {
 #if !defined(CONFIG_USER_ONLY)