[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 1/5] ARM BE8/BE32 semihosting and gdbstub support.
From: |
Julian Brown |
Subject: |
[Qemu-devel] [PATCH 1/5] ARM BE8/BE32 semihosting and gdbstub support. |
Date: |
Thu, 3 Nov 2016 10:30:54 -0700 |
This patch improves support for semihosting and debugging with the
in-built gdbstub for ARM system-mode emulation in big-endian mode (either
BE8 or BE32), after the fairly recent changes to allow a single QEMU
binary to deal with each of LE, BE8 and BE32 modes in one. It's only
currently good for little-endian host systems. The relevant use case
is using QEMU as a "bare metal" instruction-set simulator, e.g. for
toolchain testing.
For semihosting, the softmmu-semi.h file is overridden with an
ARM-specific version that knows about byte-swapping target memory into
host order -- including that which has been byte-swapped at load time
for BE32 mode.
For the gdbstub, we'd like to be able to invoke QEMU from GDB like:
(gdb) target remote | arm-qemu-system -cpu=foo [options] /dev/null
(gdb) load
(gdb) ...
which unfortunately bypasses the probing of the loaded ELF file (since
it's just /dev/null) to determine whether to use BE8/BE32 mode. So,
I added some "virtual" CPUs to set the endian type instead (arm926-be,
cortex-a15-be for BE32/BE8 mode respectively), from the reset value
of the SCTLR. This is kind of like having a configuration input to the
CPU on some hardware board to select endianness, which is a completely
legitimate thing to have, even if the implementation as-is is not really
ideal from a software-engineering standpoint. It suffices for our current
use-case though.
Signed-off-by: Julian Brown <address@hidden>
---
hw/arm/boot.c | 16 ++++-
include/exec/softmmu-arm-semi.h | 148 ++++++++++++++++++++++++++++++++++++++++
target-arm/arm-semi.c | 2 +-
target-arm/cpu.c | 49 +++++++++++++
target-arm/gdbstub.c | 42 ++++++++++++
5 files changed, 255 insertions(+), 2 deletions(-)
create mode 100644 include/exec/softmmu-arm-semi.h
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 942416d..644fdaf 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -894,7 +894,21 @@ static void arm_load_kernel_notify(Notifier *notifier,
void *data)
entry = info->loader_start + kernel_load_offset;
kernel_size = load_image_targphys(info->kernel_filename, entry,
info->ram_size - kernel_load_offset);
- is_linux = 1;
+ if (kernel_size > 0) {
+ is_linux = 1;
+ } else {
+ /* We've been launched with a kernel of /dev/null or similar.
+ * Infer endianness from the reset value of the SCTLR for this
+ * CPU/board (FIXME: a way of configuring this more nicely).
+ */
+ if (!arm_feature(&cpu->env, ARM_FEATURE_V7) &&
+ (cpu->reset_sctlr & SCTLR_B) != 0)
+ info->endianness = ARM_ENDIANNESS_BE32;
+ else if ((cpu->reset_sctlr & SCTLR_EE) != 0)
+ info->endianness = ARM_ENDIANNESS_BE8;
+ else
+ info->endianness = ARM_ENDIANNESS_LE;
+ }
}
if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
diff --git a/include/exec/softmmu-arm-semi.h b/include/exec/softmmu-arm-semi.h
new file mode 100644
index 0000000..e9ddb21
--- /dev/null
+++ b/include/exec/softmmu-arm-semi.h
@@ -0,0 +1,148 @@
+/*
+ * Helper routines to provide target memory access for ARM semihosting
+ * syscalls in system emulation mode.
+ *
+ * Copyright (c) 2007 CodeSourcery, (c) 2016 Mentor Graphics
+ *
+ * This code is licensed under the GPL
+ */
+
+#ifndef SOFTMMU_ARM_SEMI_H
+#define SOFTMMU_ARM_SEMI_H 1
+
+/* In BE32 system mode, the CPU-specific memory_rw_debug method will arrange to
+ * perform byteswapping on the target memory, so that it appears to the host as
+ * it appears to the emulated CPU. Memory is read verbatim in BE8 mode. (In
+ * other words, this function arranges so that BUF has the same format in both
+ * BE8 and BE32 system mode.)
+ */
+
+static inline int armsemi_memory_rw_debug(CPUState *cpu, target_ulong addr,
+ uint8_t *buf, int len, bool is_write)
+{
+ CPUClass *cc = CPU_GET_CLASS(cpu);
+
+ if (cc->memory_rw_debug) {
+ return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+ return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+}
+
+/* In big-endian mode (either BE8 or BE32), values larger than a byte will be
+ * transferred to/from memory in big-endian format. Assuming we're on a
+ * little-endian host machine, such values will need to be byteswapped before
+ * and after the host processes them.
+ *
+ * This means that byteswapping will occur *twice* in BE32 mode for
+ * halfword/word reads/writes.
+ */
+
+static inline bool arm_bswap_needed(CPUARMState *env)
+{
+#ifdef HOST_WORDS_BIGENDIAN
+#error HOST_WORDS_BIGENDIAN is not supported for ARM semihosting at the moment.
+#else
+ return arm_sctlr_b(env) || (env->uncached_cpsr & CPSR_E) != 0;
+#endif
+}
+
+static inline uint64_t softmmu_tget64(CPUArchState *env, target_ulong addr)
+{
+ uint64_t val;
+
+ armsemi_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 0);
+ if (arm_bswap_needed(env)) {
+ return bswap64(val);
+ } else {
+ return val;
+ }
+}
+
+static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr)
+{
+ uint32_t val;
+
+ armsemi_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 0);
+ if (arm_bswap_needed(env)) {
+ return bswap32(val);
+ } else {
+ return val;
+ }
+}
+
+static inline uint32_t softmmu_tget8(CPUArchState *env, target_ulong addr)
+{
+ uint8_t val;
+ armsemi_memory_rw_debug(ENV_GET_CPU(env), addr, &val, 1, 0);
+ return val;
+}
+
+#define get_user_u64(arg, p) ({ arg = softmmu_tget64(env, p); 0; })
+#define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; })
+#define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; })
+#define get_user_ual(arg, p) get_user_u32(arg, p)
+
+static inline void softmmu_tput64(CPUArchState *env,
+ target_ulong addr, uint64_t val)
+{
+ if (arm_bswap_needed(env)) {
+ val = bswap64(val);
+ }
+ cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 1);
+}
+
+static inline void softmmu_tput32(CPUArchState *env,
+ target_ulong addr, uint32_t val)
+{
+ if (arm_bswap_needed(env)) {
+ val = bswap32(val);
+ }
+ armsemi_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 1);
+}
+#define put_user_u64(arg, p) ({ softmmu_tput64(env, p, arg) ; 0; })
+#define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; })
+#define put_user_ual(arg, p) put_user_u32(arg, p)
+
+static void *softmmu_lock_user(CPUArchState *env,
+ target_ulong addr, target_ulong len, int copy)
+{
+ uint8_t *p;
+ /* TODO: Make this something that isn't fixed size. */
+ p = malloc(len);
+ if (p && copy) {
+ armsemi_memory_rw_debug(ENV_GET_CPU(env), addr, p, len, 0);
+ }
+ return p;
+}
+#define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy)
+static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr)
+{
+ char *p;
+ char *s;
+ uint8_t c;
+ /* TODO: Make this something that isn't fixed size. */
+ s = p = malloc(1024);
+ if (!s) {
+ return NULL;
+ }
+ do {
+ armsemi_memory_rw_debug(ENV_GET_CPU(env), addr, &c, 1, 0);
+ addr++;
+ *(p++) = c;
+ } while (c);
+ return s;
+}
+#define lock_user_string(p) softmmu_lock_user_string(env, p)
+static void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr,
+ target_ulong len)
+{
+ uint8_t *pc = p;
+ if (len) {
+ armsemi_memory_rw_debug(ENV_GET_CPU(env), addr, p, len, 1);
+ }
+ free(p);
+}
+
+#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
+
+#endif
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index 7cac873..a9cf5f2 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -114,7 +114,7 @@ static inline uint32_t set_swi_errno(CPUARMState *env,
uint32_t code)
return code;
}
-#include "exec/softmmu-semi.h"
+#include "exec/softmmu-arm-semi.h"
#endif
static target_ulong arm_semi_syscall_len;
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 15a174f..2918b24 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -32,6 +32,7 @@
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
+#include "exec/cpu-common.h"
static void arm_cpu_set_pc(CPUState *cs, vaddr value)
{
@@ -780,6 +781,17 @@ static void arm926_initfn(Object *obj)
cpu->reset_sctlr = 0x00090078;
}
+/* FIXME: This is likely not the best way to override or specify endianness
+ * for a CPU/board.
+ */
+static void arm926_be_initfn(Object *obj)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ arm926_initfn(obj);
+ cpu->reset_sctlr |= SCTLR_B;
+}
+
static void arm946_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
@@ -1248,6 +1260,17 @@ static void cortex_a15_initfn(Object *obj)
define_arm_cp_regs(cpu, cortexa15_cp_reginfo);
}
+/* FIXME: As arm926_be_initfn, this is likely not the best way to override or
+ * specify endianness for a CPU/board.
+ */
+static void cortex_a15_be_initfn(Object *obj)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ cortex_a15_initfn(obj);
+ cpu->reset_sctlr |= SCTLR_EE;
+}
+
static void ti925t_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
@@ -1442,6 +1465,7 @@ typedef struct ARMCPUInfo {
static const ARMCPUInfo arm_cpus[] = {
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
{ .name = "arm926", .initfn = arm926_initfn },
+ { .name = "arm926-be", .initfn = arm926_be_initfn },
{ .name = "arm946", .initfn = arm946_initfn },
{ .name = "arm1026", .initfn = arm1026_initfn },
/* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
@@ -1461,6 +1485,7 @@ static const ARMCPUInfo arm_cpus[] = {
{ .name = "cortex-a8", .initfn = cortex_a8_initfn },
{ .name = "cortex-a9", .initfn = cortex_a9_initfn },
{ .name = "cortex-a15", .initfn = cortex_a15_initfn },
+ { .name = "cortex-a15-be", .initfn = cortex_a15_be_initfn },
{ .name = "ti925t", .initfn = ti925t_initfn },
{ .name = "sa1100", .initfn = sa1100_initfn },
{ .name = "sa1110", .initfn = sa1110_initfn },
@@ -1519,6 +1544,27 @@ static gchar *arm_gdb_arch_name(CPUState *cs)
return g_strdup("arm");
}
+#ifndef CONFIG_USER_ONLY
+static int arm_cpu_memory_rw_debug(CPUState *cpu, vaddr address,
+ uint8_t *buf, int len, bool is_write)
+{
+ target_ulong addr = address;
+ ARMCPU *armcpu = ARM_CPU(cpu);
+ CPUARMState *env = &armcpu->env;
+
+ if (arm_sctlr_b(env)) {
+ target_ulong i;
+ for (i = 0; i < len; i++) {
+ cpu_memory_rw_debug(cpu, (addr + i) ^ 3, &buf[i], 1, is_write);
+ }
+ } else {
+ cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
+ }
+
+ return 0;
+}
+#endif
+
static void arm_cpu_class_init(ObjectClass *oc, void *data)
{
ARMCPUClass *acc = ARM_CPU_CLASS(oc);
@@ -1536,6 +1582,9 @@ static void arm_cpu_class_init(ObjectClass *oc, void
*data)
cc->has_work = arm_cpu_has_work;
cc->cpu_exec_interrupt = arm_cpu_exec_interrupt;
cc->dump_state = arm_cpu_dump_state;
+#if !defined(CONFIG_USER_ONLY)
+ cc->memory_rw_debug = arm_cpu_memory_rw_debug;
+#endif
cc->set_pc = arm_cpu_set_pc;
cc->gdb_read_register = arm_cpu_gdb_read_register;
cc->gdb_write_register = arm_cpu_gdb_write_register;
diff --git a/target-arm/gdbstub.c b/target-arm/gdbstub.c
index 04c1208..1e9fe68 100644
--- a/target-arm/gdbstub.c
+++ b/target-arm/gdbstub.c
@@ -21,6 +21,7 @@
#include "qemu-common.h"
#include "cpu.h"
#include "exec/gdbstub.h"
+#include "exec/softmmu-arm-semi.h"
/* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect
whatever the target description contains. Due to a historical mishap
@@ -32,10 +33,22 @@ int arm_cpu_gdb_read_register(CPUState *cs, uint8_t
*mem_buf, int n)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
+#ifndef CONFIG_USER_ONLY
+ bool targ_bigendian = arm_bswap_needed(env);
+#endif
if (n < 16) {
/* Core integer register. */
+#ifdef CONFIG_USER_ONLY
return gdb_get_reg32(mem_buf, env->regs[n]);
+#else
+ if (targ_bigendian) {
+ stl_be_p(mem_buf, env->regs[n]);
+ } else {
+ stl_le_p(mem_buf, env->regs[n]);
+ }
+ return 4;
+#endif
}
if (n < 24) {
/* FPA registers. */
@@ -51,10 +64,28 @@ int arm_cpu_gdb_read_register(CPUState *cs, uint8_t
*mem_buf, int n)
if (gdb_has_xml) {
return 0;
}
+#ifdef CONFIG_USER_ONLY
return gdb_get_reg32(mem_buf, 0);
+#else
+ if (targ_bigendian) {
+ stl_be_p(mem_buf, 0);
+ } else {
+ stl_le_p(mem_buf, 0);
+ }
+ return 4;
+#endif
case 25:
/* CPSR */
+#ifdef CONFIG_USER_ONLY
return gdb_get_reg32(mem_buf, cpsr_read(env));
+#else
+ if (targ_bigendian) {
+ stl_be_p(mem_buf, cpsr_read(env));
+ } else {
+ stl_le_p(mem_buf, cpsr_read(env));
+ }
+ return 4;
+#endif
}
/* Unknown register. */
return 0;
@@ -65,8 +96,19 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t
*mem_buf, int n)
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
uint32_t tmp;
+#ifndef CONFIG_USER_ONLY
+ bool targ_bigendian = arm_bswap_needed(env);
+#endif
+#ifdef CONFIG_USER_ONLY
tmp = ldl_p(mem_buf);
+#else
+ if (targ_bigendian) {
+ tmp = ldl_be_p(mem_buf);
+ } else {
+ tmp = ldl_le_p(mem_buf);
+ }
+#endif
/* Mask out low bit of PC to workaround gdb bugs. This will probably
cause problems if we ever implement the Jazelle DBX extensions. */
--
1.9.1
[Qemu-devel] [PATCH 3/5] Fix arm_semi_flen_cb for BE32 system mode., Julian Brown, 2016/11/03
[Qemu-devel] [PATCH 4/5] ARM BE32 watchpoint fix., Julian Brown, 2016/11/03