qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 3/4] target-mips: Add support for MDI semihosting


From: Maciej W. Rozycki
Subject: [Qemu-devel] [PATCH 3/4] target-mips: Add support for MDI semihosting
Date: Thu, 11 Dec 2014 15:50:45 +0000
User-agent: Alpine 1.10 (DEB 962 2008-03-14)

Add support for MDI semihosting and for loading executables from GDB 
through the GDB stub rather via the command line and the `-kernel' 
option.  Use the `qSymbol' RSP packet to interrogate GDB about the 
`_mdi_syscall' semihosting entry point.

Fall back to physical memory mapping for `-kernel /dev/null' to work and 
to set up the MMU in preparation to loading an executable from GDB.

Signed-off-by: Nathan Froyd <address@hidden>
Signed-off-by: Maciej W. Rozycki <address@hidden>
---
qemu-mips-semihosting.diff
Index: qemu-git-trunk/gdbstub.c
===================================================================
--- qemu-git-trunk.orig/gdbstub.c       2014-12-10 20:43:10.000000000 +0000
+++ qemu-git-trunk/gdbstub.c    2014-12-10 20:51:44.628959510 +0000
@@ -1092,6 +1092,36 @@ static int gdb_handle_packet(GDBState *s
                 put_packet(s, buf);
             }
             break;
+        } else if (strncmp(p, "Symbol:", 7) == 0) {
+#if defined(TARGET_MIPS) && !defined(CONFIG_USER_ONLY)
+#define MDI_SYSCALL_SYMBOL "_mdi_syscall"
+            if (strcmp(p+7, ":") == 0) {
+                /* GDB is telling us we can ask for symbols.  Look for
+                   _mdi_syscall.  */
+                memtohex((char *)mem_buf, (const uint8_t *)MDI_SYSCALL_SYMBOL,
+                         strlen(MDI_SYSCALL_SYMBOL));
+                mem_buf[strlen(MDI_SYSCALL_SYMBOL)*2] = 0;
+                snprintf(buf, sizeof(buf), "qSymbol:%s", mem_buf);
+                put_packet(s, buf);
+                break;
+            } else {
+                /* A response from a previous query.  We only send one
+                   query so it must be _mdi_syscall.  */
+                if (*(p+7) != ':') {
+                    addr = strtoull(p+7, (char **)&p, 16);
+                    hextomem(mem_buf, p+1, strlen(MDI_SYSCALL_SYMBOL)*2);
+
+                    if (memcmp(mem_buf, MDI_SYSCALL_SYMBOL,
+                               strlen(MDI_SYSCALL_SYMBOL)) == 0) {
+                        install_semihosting_breakpoint(s->c_cpu, addr);
+                    }
+                }
+            }
+            /* All done, regardless of whether we got the right symbol.  */
+            put_packet(s, "OK");
+            break;
+#undef MDI_SYSCALL_SYMBOL
+#endif
         }
 #ifdef CONFIG_USER_ONLY
         else if (strncmp(p, "Offsets", 7) == 0) {
Index: qemu-git-trunk/hw/core/loader.c
===================================================================
--- qemu-git-trunk.orig/hw/core/loader.c        2014-12-10 20:42:12.000000000 
+0000
+++ qemu-git-trunk/hw/core/loader.c     2014-12-10 20:44:25.587693472 +0000
@@ -325,9 +325,12 @@ const char *load_elf_strerror(int error)
 }
 
 /* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
-             void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
-             uint64_t *highaddr, int big_endian, int elf_machine, int 
clear_lsb)
+int load_elf_introspect(const char *filename,
+                        uint64_t (*translate_fn)(void *, uint64_t),
+                        void *translate_opaque,
+                        uint64_t *pentry, uint64_t *lowaddr, uint64_t 
*highaddr,
+                        int big_endian, int elf_machine, int clear_lsb,
+                        section_callback_t section_callback)
 {
     int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
     uint8_t e_ident[EI_NIDENT];
@@ -366,10 +369,12 @@ int load_elf(const char *filename, uint6
     lseek(fd, 0, SEEK_SET);
     if (e_ident[EI_CLASS] == ELFCLASS64) {
         ret = load_elf64(filename, fd, translate_fn, translate_opaque, 
must_swab,
-                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+                         pentry, lowaddr, highaddr, elf_machine, clear_lsb,
+                         section_callback);
     } else {
         ret = load_elf32(filename, fd, translate_fn, translate_opaque, 
must_swab,
-                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+                         pentry, lowaddr, highaddr, elf_machine, clear_lsb,
+                         section_callback);
     }
 
  fail:
@@ -377,6 +382,17 @@ int load_elf(const char *filename, uint6
     return ret;
 }
 
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
+             void *translate_opaque,
+             uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr,
+             int big_endian, int elf_machine, int clear_lsb)
+{
+    return load_elf_introspect(filename, translate_fn, translate_opaque,
+                               pentry, lowaddr, highaddr,
+                               big_endian, elf_machine, clear_lsb, NULL);
+}
+
 static void bswap_uboot_header(uboot_image_header_t *hdr)
 {
 #ifndef HOST_WORDS_BIGENDIAN
Index: qemu-git-trunk/hw/mips/mips_malta.c
===================================================================
--- qemu-git-trunk.orig/hw/mips/mips_malta.c    2014-12-10 20:42:12.000000000 
+0000
+++ qemu-git-trunk/hw/mips/mips_malta.c 2014-12-10 20:56:26.157522237 +0000
@@ -56,6 +56,8 @@
 
 //#define DEBUG_BOARD_INIT
 
+#define KERNEL_LOAD_ADDR 0x00100000
+
 #define ENVP_ADDR              0x80002000l
 #define ENVP_NB_ENTRIES                16
 #define ENVP_ENTRY_SIZE                256
@@ -772,6 +774,7 @@ static void GCC_FMT_ATTR(3, 4) prom_set(
 static int64_t load_kernel (void)
 {
     int64_t kernel_entry, kernel_high;
+    int kernel_size;
     long initrd_size;
     ram_addr_t initrd_offset;
     int big_endian;
@@ -786,9 +789,19 @@ static int64_t load_kernel (void)
     big_endian = 0;
 #endif
 
-    if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
-                 (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
-                 big_endian, ELF_MACHINE, 1) < 0) {
+    kernel_size = load_elf(loaderparams.kernel_filename,
+                           cpu_mips_kseg0_to_phys, NULL,
+                           (uint64_t *)&kernel_entry, NULL,
+                           (uint64_t *)&kernel_high,
+                           big_endian, ELF_MACHINE, 1);
+    if (kernel_size < 0) {
+        kernel_size = load_image_targphys(loaderparams.kernel_filename,
+                                          KERNEL_LOAD_ADDR,
+                                          ram_size - KERNEL_LOAD_ADDR);
+        kernel_high = KERNEL_LOAD_ADDR + kernel_size;
+        kernel_entry = KERNEL_LOAD_ADDR;
+    }
+    if (kernel_size < 0) {
         fprintf(stderr, "qemu: could not load kernel '%s'\n",
                 loaderparams.kernel_filename);
         exit(1);
Index: qemu-git-trunk/hw/mips/mips_mipssim.c
===================================================================
--- qemu-git-trunk.orig/hw/mips/mips_mipssim.c  2014-12-10 20:42:12.000000000 
+0000
+++ qemu-git-trunk/hw/mips/mips_mipssim.c       2014-12-10 20:51:44.628959510 
+0000
@@ -40,6 +40,8 @@
 #include "qemu/error-report.h"
 #include "sysemu/qtest.h"
 
+#define KERNEL_LOAD_ADDR 0x00100000
+
 static struct _loaderparams {
     int ram_size;
     const char *kernel_filename;
@@ -47,12 +49,75 @@ static struct _loaderparams {
     const char *initrd_filename;
 } loaderparams;
 
+static target_ulong mdi_semihost_bkpt;
+
+#define ENTWORDS 2
+
+static void find_sdeosabi_section(int fd, int elf_class, int must_swab,
+                                  uint64_t size, uint64_t offset,
+                                  char *name)
+{
+    const int entsize = ENTWORDS * (elf_class / 8);
+
+    if (semihosting_enabled &&
+        size >= entsize &&
+        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 pointer-size 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.  */
+            union {
+                uint64_t u64[ENTWORDS];
+                int64_t  i64[ENTWORDS];
+                uint32_t u32[ENTWORDS];
+                int32_t  i32[ENTWORDS];
+            } bkpt_info;
+
+            if (read(fd, &bkpt_info, entsize) == entsize) {
+                if (elf_class == 32) {
+                    if (must_swab) {
+                        bswap32s(&bkpt_info.u32[0]);
+                        bswap32s(&bkpt_info.u32[1]);
+                    }
+
+                    if (bkpt_info.i32[0] == 2 && bkpt_info.i32[1]) {
+                        mdi_semihost_bkpt = bkpt_info.i32[1];
+                        break;
+                    }
+                } else {
+                    if (must_swab) {
+                        bswap64s(&bkpt_info.u64[0]);
+                        bswap64s(&bkpt_info.u64[1]);
+                    }
+
+                    if (bkpt_info.i64[0] == 2 && bkpt_info.i64[1]) {
+                        mdi_semihost_bkpt = bkpt_info.i64[1];
+                        break;
+                    }
+                }
+            } else {
+                break;
+            }
+
+            section_offset += entsize;
+        }
+    }
+}
+
 typedef struct ResetData {
     MIPSCPU *cpu;
     uint64_t vector;
 } ResetData;
 
-static int64_t load_kernel(void)
+static int64_t load_kernel(CPUMIPSState *env)
 {
     int64_t entry, kernel_high;
     long kernel_size;
@@ -66,10 +131,20 @@ static int64_t load_kernel(void)
     big_endian = 0;
 #endif
 
-    kernel_size = load_elf(loaderparams.kernel_filename, 
cpu_mips_kseg0_to_phys,
-                           NULL, (uint64_t *)&entry, NULL,
-                           (uint64_t *)&kernel_high, big_endian,
-                           ELF_MACHINE, 1);
+    kernel_size = load_elf_introspect(loaderparams.kernel_filename,
+                                      cpu_mips_kseg0_to_phys, NULL,
+                                      (uint64_t *)&entry,
+                                      NULL,
+                                      (uint64_t *)&kernel_high,
+                                      big_endian, ELF_MACHINE, 1,
+                                      find_sdeosabi_section);
+    if (kernel_size < 0) {
+        kernel_size = load_image_targphys(loaderparams.kernel_filename,
+                                          KERNEL_LOAD_ADDR,
+                                          ram_size - KERNEL_LOAD_ADDR);
+        kernel_high = KERNEL_LOAD_ADDR + kernel_size;
+        entry = KERNEL_LOAD_ADDR;
+    }
     if (kernel_size >= 0) {
         if ((entry & ~0x7fffffffULL) == 0x80000000)
             entry = (int32_t)entry;
@@ -79,6 +154,11 @@ static int64_t load_kernel(void)
         exit(1);
     }
 
+    /* set up semihosting */
+    if (semihosting_enabled && mdi_semihost_bkpt) {
+        install_semihosting_breakpoint(ENV_GET_CPU(env), mdi_semihost_bkpt);
+    }
+
     /* load initrd */
     initrd_size = 0;
     initrd_offset = 0;
@@ -209,7 +289,7 @@ mips_mipssim_init(MachineState *machine)
         loaderparams.kernel_filename = kernel_filename;
         loaderparams.kernel_cmdline = kernel_cmdline;
         loaderparams.initrd_filename = initrd_filename;
-        reset_info->vector = load_kernel();
+        reset_info->vector = load_kernel(env);
     }
 
     /* Init CPU internal devices. */
Index: qemu-git-trunk/include/exec/softmmu-semi.h
===================================================================
--- qemu-git-trunk.orig/include/exec/softmmu-semi.h     2014-12-10 
20:43:37.000000000 +0000
+++ qemu-git-trunk/include/exec/softmmu-semi.h  2014-12-10 21:05:49.227897642 
+0000
@@ -9,6 +9,12 @@
 #ifndef SOFTMMU_SEMI_H
 #define SOFTMMU_SEMI_H 1
 
+#ifdef __GNUC__
+#define SOFTMMU_UNUSED __attribute__ ((unused))
+#else
+#define SOFTMMU_UNUSED
+#endif
+
 static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr)
 {
     uint32_t val;
@@ -67,6 +73,21 @@ static char *softmmu_lock_user_string(CP
     return s;
 }
 #define lock_user_string(p) softmmu_lock_user_string(env, p)
+SOFTMMU_UNUSED
+static int softmmu_target_strlen(CPUArchState *env, target_ulong addr)
+{
+    uint8_t c;
+    int len;
+
+    len = 0;
+    do {
+        cpu_memory_rw_debug(ENV_GET_CPU(env), addr + len, &c, 1, 0);
+        len++;
+    } while (c);
+
+    return len - 1;
+}
+#define target_strlen(p) softmmu_target_strlen(env, p)
 static void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr,
                                 target_ulong len)
 {
@@ -77,4 +98,6 @@ static void softmmu_unlock_user(CPUArchS
 }
 #define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
 
+#undef SOFTMMU_UNUSED
+
 #endif
Index: qemu-git-trunk/include/hw/elf_ops.h
===================================================================
--- qemu-git-trunk.orig/include/hw/elf_ops.h    2014-12-10 20:42:12.000000000 
+0000
+++ qemu-git-trunk/include/hw/elf_ops.h 2014-12-10 20:51:45.128079709 +0000
@@ -97,8 +97,14 @@ static int glue(symcmp, SZ)(const void *
         : ((sym0->st_value > sym1->st_value) ? 1 : 0);
 }
 
+/* Load function symbols for later groveling.  If SECTION_CALLBACK is
+   non-NULL, it will be called with information about each section in
+   the binary.  This interface enables to the caller to mine the binary
+   for useful information about the ABI, such as whether the CPU chosen is
+   compatible with the binary.  */
 static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
-                                  int clear_lsb)
+                                  int clear_lsb,
+                                  section_callback_t section_callback)
 {
     struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
     struct elf_sym *syms = NULL;
@@ -117,6 +123,28 @@ static int glue(load_symbols, SZ)(struct
         }
     }
 
+    /* Permit machines to grovel through the ELF file looking for
+       interesting bits of information.  */
+    if (section_callback && ehdr->e_shstrndx != SHN_UNDEF) {
+        char *shstr = NULL;
+        struct elf_shdr *shstrtab = &shdr_table[ehdr->e_shstrndx];
+
+        shstr = load_at(fd, shstrtab->sh_offset, shstrtab->sh_size);
+        if (!shstr) {
+            goto fail_callback;
+        }
+
+        for (i = 0; i < ehdr->e_shnum; i++) {
+            struct elf_shdr *sh = shdr_table + i;
+
+            section_callback(fd, SZ, must_swab, sh->sh_size, sh->sh_offset,
+                             &shstr[sh->sh_name]);
+        }
+
+    fail_callback:
+        free(shstr);
+    }
+
     symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB);
     if (!symtab)
         goto fail;
@@ -187,7 +215,8 @@ static int glue(load_elf, SZ)(const char
                               void *translate_opaque,
                               int must_swab, uint64_t *pentry,
                               uint64_t *lowaddr, uint64_t *highaddr,
-                              int elf_machine, int clear_lsb)
+                              int elf_machine, int clear_lsb,
+                              section_callback_t section_callback)
 {
     struct elfhdr ehdr;
     struct elf_phdr *phdr = NULL, *ph;
@@ -236,7 +265,7 @@ static int glue(load_elf, SZ)(const char
     if (pentry)
        *pentry = (uint64_t)(elf_sword)ehdr.e_entry;
 
-    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb);
+    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, section_callback);
 
     size = ehdr.e_phnum * sizeof(phdr[0]);
     lseek(fd, ehdr.e_phoff, SEEK_SET);
Index: qemu-git-trunk/include/hw/loader.h
===================================================================
--- qemu-git-trunk.orig/include/hw/loader.h     2014-12-10 20:42:12.000000000 
+0000
+++ qemu-git-trunk/include/hw/loader.h  2014-12-10 20:51:45.629009422 +0000
@@ -27,6 +27,14 @@ int load_elf(const char *filename, uint6
              void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
              uint64_t *highaddr, int big_endian, int elf_machine,
              int clear_lsb);
+typedef void (*section_callback_t)(int fd, int elf_class, int must_swab,
+                                   uint64_t size, uint64_t offset, char *name);
+int load_elf_introspect(const char *filename,
+                        uint64_t (*translate_fn)(void *, uint64_t),
+                        void *translate_opaque,
+                        uint64_t *pentry, uint64_t *lowaddr, uint64_t 
*highaddr,
+                        int big_endian, int elf_machine, int clear_lsb,
+                        section_callback_t callback);
 int load_aout(const char *filename, hwaddr addr, int max_sz,
               int bswap_needed, hwaddr target_page_size);
 int load_uimage(const char *filename, hwaddr *ep,
Index: qemu-git-trunk/qemu-options.hx
===================================================================
--- qemu-git-trunk.orig/qemu-options.hx 2014-12-10 20:42:12.000000000 +0000
+++ qemu-git-trunk/qemu-options.hx      2014-12-10 20:44:27.087583724 +0000
@@ -3212,11 +3212,11 @@ Set OpenBIOS nvram @var{variable} to giv
 ETEXI
 DEF("semihosting", 0, QEMU_OPTION_semihosting,
     "-semihosting    semihosting mode\n",
-    QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
+    QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_MIPS | QEMU_ARCH_XTENSA | 
QEMU_ARCH_LM32)
 STEXI
 @item -semihosting
 @findex -semihosting
-Semihosting mode (ARM, M68K, Xtensa only).
+Semihosting mode (ARM, M68K, MIPS, Xtensa only).
 ETEXI
 DEF("old-param", 0, QEMU_OPTION_old_param,
     "-old-param      old param mode\n", QEMU_ARCH_ARM)
Index: qemu-git-trunk/target-mips/Makefile.objs
===================================================================
--- qemu-git-trunk.orig/target-mips/Makefile.objs       2014-12-10 
20:43:52.000000000 +0000
+++ qemu-git-trunk/target-mips/Makefile.objs    2014-12-10 20:44:27.587970655 
+0000
@@ -1,5 +1,5 @@
 obj-y += translate.o dsp_helper.o lmi_helper.o msa_helper.o op_helper.o
 obj-y += helper.o cpu.o
-obj-y += gdbstub.o
+obj-y += gdbstub.o mips-semi.o
 obj-$(CONFIG_SOFTMMU) += machine.o
 obj-$(CONFIG_KVM) += kvm.o
Index: qemu-git-trunk/target-mips/cpu.h
===================================================================
--- qemu-git-trunk.orig/target-mips/cpu.h       2014-12-10 20:43:10.000000000 
+0000
+++ qemu-git-trunk/target-mips/cpu.h    2014-12-10 20:51:45.629009422 +0000
@@ -789,6 +789,10 @@ static inline void restore_flush_mode(CP
                       &env->active_fpu.fp_status);
 }
 
+extern target_ulong mdi_semihost_breakpoint;
+void do_mips_semihosting(CPUMIPSState *env);
+void install_semihosting_breakpoint(CPUState *cs, target_ulong bkpt_address);
+
 static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
                                         target_ulong *cs_base, int *flags)
 {
Index: qemu-git-trunk/target-mips/helper.c
===================================================================
--- qemu-git-trunk.orig/target-mips/helper.c    2014-12-10 20:43:09.000000000 
+0000
+++ qemu-git-trunk/target-mips/helper.c 2014-12-10 20:51:46.127892399 +0000
@@ -18,14 +18,13 @@
  */
 #include <stdarg.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
-#include <inttypes.h>
 #include <signal.h>
 
 #include "cpu.h"
-#include "sysemu/kvm.h"
 #include "exec/cpu_ldst.h"
+#include "sysemu/kvm.h"
+#include "sysemu/sysemu.h"
 
 enum {
     TLBRET_XI = -6,
@@ -390,6 +389,17 @@ hwaddr cpu_mips_translate_address(CPUMIP
 }
 #endif
 
+#if !defined(CONFIG_USER_ONLY)
+target_ulong mdi_semihost_breakpoint;
+
+void install_semihosting_breakpoint(CPUState *cs, target_ulong bkpt_address)
+{
+    if (semihosting_enabled) {
+        mdi_semihost_breakpoint = bkpt_address & ~(target_ulong)1;
+    }
+}
+#endif
+
 static const char * const excp_names[EXCP_LAST + 1] = {
     [EXCP_RESET] = "reset",
     [EXCP_SRESET] = "soft reset",
Index: qemu-git-trunk/target-mips/helper.h
===================================================================
--- qemu-git-trunk.orig/target-mips/helper.h    2014-12-10 20:43:09.000000000 
+0000
+++ qemu-git-trunk/target-mips/helper.h 2014-12-10 20:51:35.128974656 +0000
@@ -173,6 +173,7 @@ DEF_HELPER_0(dmt, tl)
 DEF_HELPER_0(emt, tl)
 DEF_HELPER_1(dvpe, tl, env)
 DEF_HELPER_1(evpe, tl, env)
+DEF_HELPER_1(semihosting_call, void, env)
 #endif /* !CONFIG_USER_ONLY */
 
 /* microMIPS functions */
Index: qemu-git-trunk/target-mips/mips-semi.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ qemu-git-trunk/target-mips/mips-semi.c      2014-12-10 20:44:28.588927918 
+0000
@@ -0,0 +1,242 @@
+/*
+ * MIPS MDI semihosting syscalls
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Nathan Froyd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "cpu.h"
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "exec/gdbstub.h"
+#include "exec/softmmu-semi.h"
+
+#define HOSTED_OPEN 0
+#define HOSTED_CLOSE 1
+#define HOSTED_READ 2
+#define HOSTED_WRITE 3
+#define HOSTED_GETCHAR 4
+#define HOSTED_PUTCHAR 5
+#define HOSTED_LSEEK32 6
+#define HOSTED_GETTIME 7
+#define HOSTED_EXIT 8
+#define HOSTED_MOVED 9
+#define HOSTED_GETARGS 10
+#define HOSTED_ISATTY 11
+#define HOSTED_PROFIL 12
+#define HOSTED_SIGHOOK 13
+
+#define ARG(n) (env->active_tc.gpr[4 + (n)])
+
+static void mips_store_result(CPUMIPSState *env,
+                              target_ulong ret, target_ulong err)
+{
+    env->active_tc.PC = env->active_tc.gpr[31] & ~(target_ulong)1;
+    if (env->active_tc.gpr[31] & 1) {
+        env->hflags |= MIPS_HFLAG_M16;
+    } else {
+        env->hflags &= ~MIPS_HFLAG_M16;
+    }
+    env->active_tc.gpr[2] = ret;
+    env->active_tc.gpr[3] = err;
+}
+
+static void mips_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
+{
+    MIPSCPU *cpu = MIPS_CPU(cs);
+    CPUMIPSState *env = &cpu->env;
+
+    mips_store_result(env, ret, err);
+}
+
+#define GDB_O_RDONLY   0x0
+#define GDB_O_WRONLY   0x1
+#define GDB_O_RDWR     0x2
+#define GDB_O_APPEND   0x8
+#define GDB_O_CREAT  0x200
+#define GDB_O_TRUNC  0x400
+#define GDB_O_EXCL   0x800
+
+static int translate_openflags(int flags)
+{
+    int hf;
+
+    if (flags & GDB_O_WRONLY) {
+        hf = O_WRONLY;
+    } else if (flags & GDB_O_RDWR) {
+        hf = O_RDWR;
+    } else {
+        hf = O_RDONLY;
+    }
+
+    if (flags & GDB_O_APPEND) {
+        hf |= O_APPEND;
+    }
+    if (flags & GDB_O_CREAT) {
+        hf |= O_CREAT;
+    }
+    if (flags & GDB_O_TRUNC) {
+        hf |= O_TRUNC;
+    }
+    if (flags & GDB_O_EXCL) {
+        hf |= O_EXCL;
+    }
+
+    return hf;
+}
+
+void do_mips_semihosting(CPUMIPSState *env)
+{
+    target_ulong result;
+    void *p;
+    uint32_t len;
+    target_ulong err = 0;
+    char *s;
+
+    switch (env->active_tc.gpr[2]) {
+    case HOSTED_OPEN:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "open,%s,%x,%x", ARG(0),
+                           target_strlen(ARG(0))+1, ARG(1), ARG(2));
+            return;
+        } else {
+            s = lock_user_string(ARG(0));
+            if (s) {
+                result = open(s, translate_openflags(ARG(1)), ARG(2));
+                unlock_user(s, ARG(0), 0);
+            } else {
+                result = -1;
+            }
+        }
+        break;
+    case HOSTED_CLOSE:
+        /* Ignore attempts to close stdin/out/err */
+        if (ARG(0) > 2) {
+            if (use_gdb_syscalls()) {
+                gdb_do_syscall(mips_semi_cb, "close,%x", ARG(0));
+                return;
+            } else {
+                result = close(ARG(0));
+            }
+        } else {
+            result = 0;
+        }
+        break;
+    case HOSTED_READ:
+        len = ARG(2);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "read,%x,%x,%x",
+                           ARG(0), ARG(1), len);
+            return;
+        } else {
+            p = lock_user(VERIFY_WRITE, ARG(1), len, 0);
+            if (p) {
+                result = read(ARG(0), p, len);
+                unlock_user(p, ARG(1), len);
+            } else {
+                result = -1;
+            }
+        }
+        break;
+    case HOSTED_WRITE:
+        len = ARG(2);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "write,%x,%x,%x",
+                           ARG(0), ARG(1), len);
+            return;
+        } else {
+            p = lock_user(VERIFY_READ, ARG(1), len, 1);
+            if (p) {
+                result = write(ARG(0), p, len);
+                unlock_user(p, ARG(1), len);
+            } else {
+                result = -1;
+            }
+        }
+        break;
+    case HOSTED_LSEEK32:
+        {
+            off_t off = (target_long)ARG(1);
+            if (use_gdb_syscalls()) {
+                gdb_do_syscall(mips_semi_cb, "lseek,%x,%lx,%x",
+                               ARG(0), off, ARG(2));
+                return;
+            } else {
+                off = lseek(ARG(0), off, ARG(2));
+                result = (uint32_t)off;
+            }
+        }
+        break;
+    case HOSTED_GETTIME:
+        {
+            qemu_timeval tv;
+            result = qemu_gettimeofday(&tv);
+            if (!result) {
+                result = tv.tv_sec;
+                err = tv.tv_usec;
+            } else {
+                result = -1;
+                err = errno;
+            }
+        }
+        break;
+    case HOSTED_EXIT:
+        /* FIXME: ideally we want to inform gdb about program
+           exit whenever gdb is connected, even if syscalls
+           are not handled by gdb.  */
+        if (use_gdb_syscalls()) {
+            gdb_exit(env, ARG(0));
+        }
+        exit(ARG(0));
+        break;
+    case HOSTED_ISATTY:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(mips_semi_cb, "isatty,%x", ARG(0));
+            return;
+        } else {
+            result = isatty(ARG(0));
+        }
+        break;
+    case HOSTED_GETARGS:
+        /* argc gets placed in A0, argv gets copied onto the stack and
+           the address of the copy placed in A1.  We have nothing to
+           provide in terms of argc/argv, so just stuff NULL in
+           each.  */
+        ARG(1) = ARG(0) = 0;
+        result = 0;
+        break;
+    case HOSTED_GETCHAR:
+    case HOSTED_PUTCHAR:
+    case HOSTED_MOVED:
+    case HOSTED_PROFIL:
+    case HOSTED_SIGHOOK:
+    default:
+        result = -1;
+        err = 88;               /* ENOSYS */
+        break;
+    }
+
+    mips_store_result(env, result, err);
+}
Index: qemu-git-trunk/target-mips/op_helper.c
===================================================================
--- qemu-git-trunk.orig/target-mips/op_helper.c 2014-12-10 20:43:10.000000000 
+0000
+++ qemu-git-trunk/target-mips/op_helper.c      2014-12-10 20:51:35.128974656 
+0000
@@ -1764,6 +1764,11 @@ target_ulong helper_evpe(CPUMIPSState *e
     }
     return prev;
 }
+
+void helper_semihosting_call(CPUMIPSState *env)
+{
+    do_mips_semihosting(env);
+}
 #endif /* !CONFIG_USER_ONLY */
 
 void helper_fork(target_ulong arg1, target_ulong arg2)
Index: qemu-git-trunk/target-mips/translate.c
===================================================================
--- qemu-git-trunk.orig/target-mips/translate.c 2014-12-10 20:43:10.000000000 
+0000
+++ qemu-git-trunk/target-mips/translate.c      2014-12-10 20:51:35.128974656 
+0000
@@ -29,6 +29,7 @@
 #include "exec/helper-proto.h"
 #include "exec/helper-gen.h"
 #include "sysemu/kvm.h"
+#include "sysemu/sysemu.h"
 
 #include "trace-tcg.h"
 
@@ -19142,6 +19143,17 @@ gen_intermediate_code_internal(MIPSCPU *
                 }
             }
         }
+#ifndef CONFIG_USER_ONLY
+        if (unlikely(semihosting_enabled) &&
+            mdi_semihost_breakpoint &&
+            ctx.pc == mdi_semihost_breakpoint) {
+            ctx.bstate = BS_EXCP;
+            gen_helper_semihosting_call(cpu_env);
+            tcg_gen_exit_tb(0);
+            ctx.pc += 4;
+            goto done_generating;
+        }
+#endif
 
         if (search_pc) {
             j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]