* [Qemu-devel] [PATCH 1/6] sysemu: add section_callback argument to ELF loader
2009-08-03 14:45 [Qemu-devel] [PATCH 0/6] target-mips: add MDI semihosting, v2 Nathan Froyd
@ 2009-08-03 14:45 ` Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 2/6] add softmmu_target_strlen Nathan Froyd
` (4 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Nathan Froyd @ 2009-08-03 14:45 UTC (permalink / raw)
To: qemu-devel
Some targets indicate properties of the program with special sections in
the ELF file--MIPS in particular. This infrastructure is useful for
grovelling through those sections.
Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
elf_ops.h | 34 +++++++++++++++++++++++++++++++---
loader.c | 19 ++++++++++++++-----
sysemu.h | 5 +++++
3 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/elf_ops.h b/elf_ops.h
index 699651c..0acbcc7 100644
--- a/elf_ops.h
+++ b/elf_ops.h
@@ -98,7 +98,13 @@ static int glue(symcmp, SZ)(const void *s0, const void *s1)
: ((sym0->st_value > sym1->st_value) ? 1 : 0);
}
-static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
+/* 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,
+ section_callback_t section_callback)
{
struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
struct elf_sym *syms = NULL;
@@ -117,6 +123,27 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
}
}
+ /* 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, 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;
@@ -179,7 +206,8 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
static int glue(load_elf, SZ)(int fd, int64_t address_offset,
int must_swab, uint64_t *pentry,
- uint64_t *lowaddr, uint64_t *highaddr)
+ uint64_t *lowaddr, uint64_t *highaddr,
+ section_callback_t section_callback)
{
struct elfhdr ehdr;
struct elf_phdr *phdr = NULL, *ph;
@@ -213,7 +241,7 @@ static int glue(load_elf, SZ)(int fd, int64_t address_offset,
if (pentry)
*pentry = (uint64_t)(elf_sword)ehdr.e_entry;
- glue(load_symbols, SZ)(&ehdr, fd, must_swab);
+ glue(load_symbols, SZ)(&ehdr, fd, must_swab, section_callback);
size = ehdr.e_phnum * sizeof(phdr[0]);
lseek(fd, ehdr.e_phoff, SEEK_SET);
diff --git a/loader.c b/loader.c
index 0cbcf9c..6f025a9 100644
--- a/loader.c
+++ b/loader.c
@@ -305,9 +305,10 @@ static void *load_at(int fd, int offset, int size)
#define SZ 64
#include "elf_ops.h"
-/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf(const char *filename, int64_t address_offset,
- uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
+int load_elf_introspect(const char *filename, int64_t address_offset,
+ uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr,
+ section_callback_t section_callback)
{
int fd, data_order, host_data_order, must_swab, ret;
uint8_t e_ident[EI_NIDENT];
@@ -342,10 +343,10 @@ int load_elf(const char *filename, int64_t address_offset,
lseek(fd, 0, SEEK_SET);
if (e_ident[EI_CLASS] == ELFCLASS64) {
ret = load_elf64(fd, address_offset, must_swab, pentry,
- lowaddr, highaddr);
+ lowaddr, highaddr, section_callback);
} else {
ret = load_elf32(fd, address_offset, must_swab, pentry,
- lowaddr, highaddr);
+ lowaddr, highaddr, section_callback);
}
close(fd);
@@ -356,6 +357,14 @@ int load_elf(const char *filename, int64_t address_offset,
return -1;
}
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, int64_t address_offset,
+ uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
+{
+ return load_elf_introspect (filename, address_offset, pentry, lowaddr,
+ highaddr, NULL);
+}
+
static void bswap_uboot_header(uboot_image_header_t *hdr)
{
#ifndef HOST_WORDS_BIGENDIAN
diff --git a/sysemu.h b/sysemu.h
index 6af88d8..b26cf40 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -241,6 +241,11 @@ int load_image(const char *filename, uint8_t *addr); /* deprecated */
int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz);
int load_elf(const char *filename, int64_t address_offset,
uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr);
+typedef void (*section_callback_t)(int fd, int must_swab,
+ uint64_t size, uint64_t offset, char *name);
+int load_elf_introspect(const char *filename, int64_t address_offset,
+ uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr, section_callback_t callback);
int load_aout(const char *filename, target_phys_addr_t addr, int max_sz);
int load_uimage(const char *filename, target_ulong *ep, target_ulong *loadaddr,
int *is_linux);
--
1.6.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 2/6] add softmmu_target_strlen
2009-08-03 14:45 [Qemu-devel] [PATCH 0/6] target-mips: add MDI semihosting, v2 Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 1/6] sysemu: add section_callback argument to ELF loader Nathan Froyd
@ 2009-08-03 14:45 ` Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 3/6] add implementation of MIPS semihosting Nathan Froyd
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Nathan Froyd @ 2009-08-03 14:45 UTC (permalink / raw)
To: qemu-devel
Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
softmmu-semi.h | 25 +++++++++++++++++++++++++
1 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/softmmu-semi.h b/softmmu-semi.h
index 79278cc..a32588a 100644
--- a/softmmu-semi.h
+++ b/softmmu-semi.h
@@ -7,6 +7,12 @@
* This code is licenced under the GPL
*/
+#ifdef __GNUC__
+#define SOFTMMU_UNUSED __attribute__ ((unused))
+#else
+#define SOFTMUU_UNUSED
+#endif
+
static inline uint32_t softmmu_tget32(CPUState *env, uint32_t addr)
{
uint32_t val;
@@ -60,6 +66,23 @@ static char *softmmu_lock_user_string(CPUState *env, uint32_t addr)
return s;
}
#define lock_user_string(p) softmmu_lock_user_string(env, p)
+
+SOFTMMU_UNUSED
+static int softmmu_target_strlen(CPUState *env, target_ulong addr)
+{
+ uint8_t c;
+ int len;
+
+ len = 0;
+ do {
+ cpu_memory_rw_debug(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(CPUState *env, void *p, target_ulong addr,
target_ulong len)
{
@@ -68,3 +91,5 @@ static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr,
free(p);
}
#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
+
+#undef SOFTMMU_UNUSED
--
1.6.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 3/6] add implementation of MIPS semihosting
2009-08-03 14:45 [Qemu-devel] [PATCH 0/6] target-mips: add MDI semihosting, v2 Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 1/6] sysemu: add section_callback argument to ELF loader Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 2/6] add softmmu_target_strlen Nathan Froyd
@ 2009-08-03 14:45 ` Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 4/6] target-mips: add MDI semihosting support to mipssim machine Nathan Froyd
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Nathan Froyd @ 2009-08-03 14:45 UTC (permalink / raw)
To: qemu-devel
Later patches will call into this file via do_mips_semihosting.
Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
Makefile.target | 1 +
mips-semi.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++++++++
target-mips/cpu.h | 1 +
3 files changed, 218 insertions(+), 0 deletions(-)
create mode 100644 mips-semi.c
diff --git a/Makefile.target b/Makefile.target
index 49ba08d..676a9f9 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -370,6 +370,7 @@ obj-mips-y += piix_pci.o parallel.o cirrus_vga.o pcspk.o $(sound-obj-y)
obj-mips-y += mipsnet.o
obj-mips-y += pflash_cfi01.o
obj-mips-y += vmware_vga.o
+obj-mips-y += mips-semi.o
ifeq ($(TARGET_BASE_ARCH), mips)
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
diff --git a/mips-semi.c b/mips-semi.c
new file mode 100644
index 0000000..77ecff3
--- /dev/null
+++ b/mips-semi.c
@@ -0,0 +1,216 @@
+
+/*
+ * 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.h"
+#include "gdbstub.h"
+#include "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(CPUState *env, target_ulong ret, target_ulong err)
+{
+ env->active_tc.PC = env->active_tc.gpr[31];
+ env->active_tc.gpr[2] = ret;
+ env->active_tc.gpr[3] = err;
+}
+
+static void mips_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+ 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(CPUState *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 {
+ if (!(s = lock_user_string(ARG(0)))) {
+ result = -1;
+ } else {
+ result = open(s, translate_openflags(ARG(1)), ARG(2));
+ }
+ unlock_user(s, ARG(0), 0);
+ }
+ 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 {
+ if (!(p = lock_user(VERIFY_WRITE, ARG(1), len, 0))) {
+ result = -1;
+ } else {
+ result = read(ARG(0), p, len);
+ unlock_user(p, ARG(1), len);
+ }
+ }
+ 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 {
+ if (!(p = lock_user(VERIFY_READ, ARG(1), len, 1))) {
+ result = -1;
+ } else {
+ result = write(ARG(0), p, len);
+ unlock_user(p, ARG(1), len);
+ }
+ }
+ 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:
+ 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);
+}
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 789176b..1510244 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -587,6 +587,7 @@ int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
void do_interrupt (CPUState *env);
void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra);
+void do_mips_semihosting(CPUState *env);
static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
{
env->active_tc.PC = tb->pc;
--
1.6.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 4/6] target-mips: add MDI semihosting support to mipssim machine
2009-08-03 14:45 [Qemu-devel] [PATCH 0/6] target-mips: add MDI semihosting, v2 Nathan Froyd
` (2 preceding siblings ...)
2009-08-03 14:45 ` [Qemu-devel] [PATCH 3/6] add implementation of MIPS semihosting Nathan Froyd
@ 2009-08-03 14:45 ` Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 5/6] enable --semihosting option for TARGET_MIPS Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 6/6] gdbstub: add qSymbol handling " Nathan Froyd
5 siblings, 0 replies; 8+ messages in thread
From: Nathan Froyd @ 2009-08-03 14:45 UTC (permalink / raw)
To: qemu-devel
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(-)
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)
--
1.6.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 5/6] enable --semihosting option for TARGET_MIPS
2009-08-03 14:45 [Qemu-devel] [PATCH 0/6] target-mips: add MDI semihosting, v2 Nathan Froyd
` (3 preceding siblings ...)
2009-08-03 14:45 ` [Qemu-devel] [PATCH 4/6] target-mips: add MDI semihosting support to mipssim machine Nathan Froyd
@ 2009-08-03 14:45 ` Nathan Froyd
2009-08-03 14:45 ` [Qemu-devel] [PATCH 6/6] gdbstub: add qSymbol handling " Nathan Froyd
5 siblings, 0 replies; 8+ messages in thread
From: Nathan Froyd @ 2009-08-03 14:45 UTC (permalink / raw)
To: qemu-devel
Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
qemu-options.hx | 2 +-
vl.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/qemu-options.hx b/qemu-options.hx
index 1b420a3..8514491 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1658,7 +1658,7 @@ DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env,
"-prom-env variable=value\n"
" set OpenBIOS nvram variables\n")
#endif
-#if defined(TARGET_ARM) || defined(TARGET_M68K)
+#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_MIPS)
DEF("semihosting", 0, QEMU_OPTION_semihosting,
"-semihosting semihosting mode\n")
#endif
diff --git a/vl.c b/vl.c
index fdd4f03..c3525e2 100644
--- a/vl.c
+++ b/vl.c
@@ -5520,7 +5520,7 @@ int main(int argc, char **argv, char **envp)
option_rom[nb_option_roms] = optarg;
nb_option_roms++;
break;
-#if defined(TARGET_ARM) || defined(TARGET_M68K)
+#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_MIPS)
case QEMU_OPTION_semihosting:
semihosting_enabled = 1;
break;
--
1.6.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 6/6] gdbstub: add qSymbol handling for TARGET_MIPS
2009-08-03 14:45 [Qemu-devel] [PATCH 0/6] target-mips: add MDI semihosting, v2 Nathan Froyd
` (4 preceding siblings ...)
2009-08-03 14:45 ` [Qemu-devel] [PATCH 5/6] enable --semihosting option for TARGET_MIPS Nathan Froyd
@ 2009-08-03 14:45 ` Nathan Froyd
5 siblings, 0 replies; 8+ messages in thread
From: Nathan Froyd @ 2009-08-03 14:45 UTC (permalink / raw)
To: qemu-devel
QEMU needs to know the address of _mdi_syscall so that breakpoints can
be set appropriately. But if QEMU is started from within GDB as:
(gdb) target remote | qemu -M mipssim -s -S ... -kernel /dev/null
...
(gdb) load
then QEMU's ELF loader never gets a chance to grovel through the ELF
file to look for the .sdeosabi section.
Therefore, the GDB stub needs to know how to ask GDB for the address of
_mdi_syscall so that the necessary breakpoint can be set.
Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
gdbstub.c | 29 +++++++++++++++++++++++++++++
1 files changed, 29 insertions(+), 0 deletions(-)
diff --git a/gdbstub.c b/gdbstub.c
index ff4c86c..ab2ce07 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1937,6 +1937,35 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
put_packet(s, buf);
}
break;
+ } else if (strncmp(p, "Symbol:", 7) == 0) {
+#if defined(TARGET_MIPS) && !defined(TARGET_MIPS64) && !defined(CONFIG_USER_ONLY)
+#define MDI_SYSCALL_SYMBOL "_mdi_syscall"
+ if (strncmp(p+7, ":", 1) == 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. */
+ 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) {
--
1.6.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 1/6] sysemu: add section_callback argument to ELF loader
2009-07-17 20:33 [Qemu-devel] [PATCH 0/6] target-mips: add MDI semihosting Nathan Froyd
@ 2009-07-17 20:33 ` Nathan Froyd
0 siblings, 0 replies; 8+ messages in thread
From: Nathan Froyd @ 2009-07-17 20:33 UTC (permalink / raw)
To: qemu-devel
Some targets indicate properties of the program with special sections in
the ELF file--MIPS in particular. This infrastructure is useful for
grovelling through those sections.
Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
elf_ops.h | 34 +++++++++++++++++++++++++++++++---
loader.c | 19 ++++++++++++++-----
sysemu.h | 5 +++++
3 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/elf_ops.h b/elf_ops.h
index 72cd83e..9bede1b 100644
--- a/elf_ops.h
+++ b/elf_ops.h
@@ -98,7 +98,13 @@ static int glue(symcmp, SZ)(const void *s0, const void *s1)
: ((sym0->st_value > sym1->st_value) ? 1 : 0);
}
-static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
+/* 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,
+ section_callback_t section_callback)
{
struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
struct elf_sym *syms = NULL;
@@ -117,6 +123,27 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
}
}
+ /* 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, 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;
@@ -179,7 +206,8 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
static int glue(load_elf, SZ)(int fd, int64_t address_offset,
int must_swab, uint64_t *pentry,
- uint64_t *lowaddr, uint64_t *highaddr)
+ uint64_t *lowaddr, uint64_t *highaddr,
+ section_callback_t section_callback)
{
struct elfhdr ehdr;
struct elf_phdr *phdr = NULL, *ph;
@@ -213,7 +241,7 @@ static int glue(load_elf, SZ)(int fd, int64_t address_offset,
if (pentry)
*pentry = (uint64_t)(elf_sword)ehdr.e_entry;
- glue(load_symbols, SZ)(&ehdr, fd, must_swab);
+ glue(load_symbols, SZ)(&ehdr, fd, must_swab, section_callback);
size = ehdr.e_phnum * sizeof(phdr[0]);
lseek(fd, ehdr.e_phoff, SEEK_SET);
diff --git a/loader.c b/loader.c
index 54580e4..7caf395 100644
--- a/loader.c
+++ b/loader.c
@@ -302,9 +302,10 @@ static void *load_at(int fd, int offset, int size)
#define SZ 64
#include "elf_ops.h"
-/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf(const char *filename, int64_t address_offset,
- uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
+int load_elf_introspect(const char *filename, int64_t address_offset,
+ uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr,
+ section_callback_t section_callback)
{
int fd, data_order, host_data_order, must_swab, ret;
uint8_t e_ident[EI_NIDENT];
@@ -339,10 +340,10 @@ int load_elf(const char *filename, int64_t address_offset,
lseek(fd, 0, SEEK_SET);
if (e_ident[EI_CLASS] == ELFCLASS64) {
ret = load_elf64(fd, address_offset, must_swab, pentry,
- lowaddr, highaddr);
+ lowaddr, highaddr, section_callback);
} else {
ret = load_elf32(fd, address_offset, must_swab, pentry,
- lowaddr, highaddr);
+ lowaddr, highaddr, section_callback);
}
close(fd);
@@ -353,6 +354,14 @@ int load_elf(const char *filename, int64_t address_offset,
return -1;
}
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, int64_t address_offset,
+ uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
+{
+ return load_elf_introspect (filename, address_offset, pentry, lowaddr,
+ highaddr, NULL);
+}
+
static void bswap_uboot_header(uboot_image_header_t *hdr)
{
#ifndef WORDS_BIGENDIAN
diff --git a/sysemu.h b/sysemu.h
index df19f02..5f685a7 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -245,6 +245,11 @@ int load_image(const char *filename, uint8_t *addr); /* deprecated */
int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz);
int load_elf(const char *filename, int64_t address_offset,
uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr);
+typedef void (*section_callback_t)(int fd, int must_swab,
+ uint64_t size, uint64_t offset, char *name);
+int load_elf_introspect(const char *filename, int64_t address_offset,
+ uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr, section_callback_t callback);
int load_aout(const char *filename, target_phys_addr_t addr, int max_sz);
int load_uimage(const char *filename, target_ulong *ep, target_ulong *loadaddr,
int *is_linux);
--
1.6.3.2
^ permalink raw reply related [flat|nested] 8+ messages in thread