diff mbox

[RFC,02/14] Added semihosting support for A64 in full-system mode

Message ID 1438793483-12721-3-git-send-email-cov@codeaurora.org
State New
Headers show

Commit Message

Christopher Covington Aug. 5, 2015, 4:51 p.m. UTC
This is for full-system only; not implemented in user mode

Written by Derek Hower.

Signed-off-by: Christopher Covington <cov@codeaurora.org>
---
 include/exec/softmmu-semi.h |  21 ++++++-
 target-arm/arm-semi.c       | 142 ++++++++++++++++++++++++++++++++++++--------
 target-arm/cpu.h            |   3 +-
 target-arm/helper-a64.c     |  28 ++++++++-
 target-arm/helper.c         |   1 +
 target-arm/internals.h      |   8 +++
 target-arm/translate-a64.c  |   2 +-
 7 files changed, 174 insertions(+), 31 deletions(-)

Comments

Peter Maydell Aug. 11, 2015, 6:16 p.m. UTC | #1
On 5 August 2015 at 17:51, Christopher Covington <cov@codeaurora.org> wrote:
> This is for full-system only; not implemented in user mode
>
> Written by Derek Hower.

> -    cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
> -    env->regs[0] = be32_to_cpu(size);
> +    if (env->aarch64) {
> +      cpu_memory_rw_debug(cs, env->pc-64+32, (uint8_t *)&size, 4, 0);
> +      env->xregs[0] = be32_to_cpu(size);
> +    } else {
> +      cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
> +      env->regs[0] = be32_to_cpu(size);
> +    }

>      case TARGET_SYS_FLEN:
>          GET_ARG(0);
>          if (use_gdb_syscalls()) {
> +          if (env->aarch64) {
> +            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
> +                           arg0, env->pc-64);
> +            return env->xregs[0];
> +
> +          } else {
>              gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
>                             arg0, env->regs[13]-64);
>              return env->regs[0];
> +          }

These two bits are badly buggy if you ever try to use this semihosting
call with gdb syscalls enabled on A64. r13 is SP, not PC, but your
A64 code is telling gdb to write the struct stat buf to guest memory
starting at pc-64, so it will corrupt the code we've just executed...

(I'm working on an inspired-by-this but rewritten patchset for
A64 semihosting, so this is just in case you were using these
patches somewhere in the interim.)

thanks
-- PMM
diff mbox

Patch

diff --git a/include/exec/softmmu-semi.h b/include/exec/softmmu-semi.h
index 8401f7d..9ab8353 100644
--- a/include/exec/softmmu-semi.h
+++ b/include/exec/softmmu-semi.h
@@ -9,6 +9,13 @@ 
 #ifndef SOFTMMU_SEMI_H
 #define SOFTMMU_SEMI_H 1
 
+static inline uint64_t softmmu_tget64(CPUArchState *env, uint64_t addr)
+{
+    uint64_t val;
+
+    cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 0);
+    return tswap64(val);
+}
 static inline uint32_t softmmu_tget32(CPUArchState *env, uint32_t addr)
 {
     uint32_t val;
@@ -24,19 +31,27 @@  static inline uint32_t softmmu_tget8(CPUArchState *env, uint32_t addr)
     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, uint64_t addr, uint64_t val)
+{
+    val = tswap64(val);
+    cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 1);
+}
+
 static inline void softmmu_tput32(CPUArchState *env, uint32_t addr, uint32_t val)
 {
     val = tswap32(val);
     cpu_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, uint32_t addr, uint32_t len,
+static void *softmmu_lock_user(CPUArchState *env, target_ulong addr, uint32_t len,
                                int copy)
 {
     uint8_t *p;
@@ -48,11 +63,11 @@  static void *softmmu_lock_user(CPUArchState *env, uint32_t addr, uint32_t len,
     return p;
 }
 #define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy)
-static char *softmmu_lock_user_string(CPUArchState *env, uint32_t addr)
+static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr)
 {
     char *p;
     char *s;
-    uint8_t c;
+    uint8_t c = 0;
     /* TODO: Make this something that isn't fixed size.  */
     s = p = malloc(1024);
     if (!s) {
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index bcc70ec..b89ea8f 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -4,6 +4,9 @@ 
  *  Copyright (c) 2005, 2007 CodeSourcery.
  *  Written by Paul Brook.
  *
+ *  Copyright (c) 2015 the Linux Foundation.
+ *  Written by Derek Hower.
+ *
  *  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
@@ -140,19 +143,35 @@  static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
 #else
 	syscall_err = err;
 #endif
-        env->regs[0] = ret;
+        if (env->aarch64) {
+          env->xregs[0] = ret;
+        } else {
+          env->regs[0] = ret;
+        }
     } else {
         /* Fixup syscalls that use nonstardard return conventions.  */
         switch (env->regs[0]) {
         case TARGET_SYS_WRITE:
         case TARGET_SYS_READ:
+          if (env->aarch64) {
+            env->xregs[0] = arm_semi_syscall_len - ret;
+          } else {
             env->regs[0] = arm_semi_syscall_len - ret;
+          }
             break;
         case TARGET_SYS_SEEK:
+          if (env->aarch64) {
+            env->xregs[0] = 0;
+          } else {
             env->regs[0] = 0;
+          }
             break;
         default:
+          if (env->aarch64) {
+            env->xregs[0] = ret;
+          } else {
             env->regs[0] = ret;
+          }
             break;
         }
     }
@@ -165,8 +184,13 @@  static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
     /* The size is always stored in big-endian order, extract
        the value. We assume the size always fit in 32 bits.  */
     uint32_t size;
-    cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
-    env->regs[0] = be32_to_cpu(size);
+    if (env->aarch64) {
+      cpu_memory_rw_debug(cs, env->pc-64+32, (uint8_t *)&size, 4, 0);
+      env->xregs[0] = be32_to_cpu(size);
+    } else {
+      cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
+      env->regs[0] = be32_to_cpu(size);
+    }
 #ifdef CONFIG_USER_ONLY
     ((TaskState *)cs->opaque)->swi_errno = err;
 #else
@@ -177,14 +201,28 @@  static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
 /* Read the input value from the argument block; fail the semihosting
  * call if the memory read fails.
  */
-#define GET_ARG(n) do {                                 \
-    if (get_user_ual(arg ## n, args + (n) * 4)) {       \
-        return (uint32_t)-1;                            \
+#define GET_ARG(n) do {                                         \
+    if (env->aarch64) {                                         \
+      if (get_user_u64(arg ## n, args + (n) * 8)) {             \
+        return (target_ulong)-1;                                \
+      }                                                         \
+    } else {                                                    \
+      if (get_user_ual(arg ## n, (uint32_t) args + (n) * 4)) {  \
+        return (target_ulong)-1;                                \
+      }                                                         \
+    }                                                           \
+  } while (0)
+
+#define SET_ARG(n, val)                                 \
+  ({                                                    \
+    if (env->aarch64) {                                 \
+      ret = put_user_u64(val, args + (n) * 8);          \
+    } else {                                            \
+      ret = put_user_ual(val, args + (n) * 4);          \
     }                                                   \
-} while (0)
-
-#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
-uint32_t do_arm_semihosting(CPUARMState *env)
+    0;                                                  \
+  })
+target_ulong do_arm_semihosting(CPUARMState *env)
 {
     target_ulong args;
     target_ulong arg0, arg1, arg2, arg3;
@@ -200,8 +238,13 @@  uint32_t do_arm_semihosting(CPUARMState *env)
     CPUARMState *ts = env;
 #endif
 
-    nr = env->regs[0];
-    args = env->regs[1];
+    if (env->aarch64) {
+      nr = env->xregs[0];
+      args = env->xregs[1];
+    } else {
+      nr = env->regs[0];
+      args = env->regs[1];
+    }
     switch (nr) {
     case TARGET_SYS_OPEN:
         GET_ARG(0);
@@ -224,9 +267,13 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
                            (int)arg2+1, gdb_open_modeflags[arg1]);
-            ret = env->regs[0];
+            if (env->aarch64) {
+              ret = env->xregs[0];
+            } else {
+              ret = env->regs[0];
+            }
         } else {
-            ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
+          ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
         }
         unlock_user(s, arg0, 0);
         return ret;
@@ -234,7 +281,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         GET_ARG(0);
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
-            return env->regs[0];
+            if (env->aarch64) {
+              return env->xregs[0];
+            } else {
+              return env->regs[0];
+            }
         } else {
             return set_swi_errno(ts, close(arg0));
         }
@@ -248,7 +299,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
           /* Write to debug console.  stderr is near enough.  */
           if (use_gdb_syscalls()) {
                 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
-                return env->regs[0];
+                if (env->aarch64) {
+                  return env->xregs[0];
+                } else {
+                  return env->regs[0];
+                }
           } else {
                 return write(STDERR_FILENO, &c, 1);
           }
@@ -260,7 +315,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         len = strlen(s);
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
-            ret = env->regs[0];
+            if (env->aarch64) {
+              ret = env->xregs[0];
+            } else {
+              ret = env->regs[0];
+            }
         } else {
             ret = write(STDERR_FILENO, s, len);
         }
@@ -274,7 +333,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         if (use_gdb_syscalls()) {
             arm_semi_syscall_len = len;
             gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
-            return env->regs[0];
+            if (env->aarch64) {
+              return env->xregs[0];
+            } else {
+              return env->regs[0];
+            }
         } else {
             s = lock_user(VERIFY_READ, arg1, len, 1);
             if (!s) {
@@ -295,7 +358,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         if (use_gdb_syscalls()) {
             arm_semi_syscall_len = len;
             gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
-            return env->regs[0];
+            if (env->aarch64) {
+              return env->xregs[0];
+            } else {
+              return env->regs[0];
+            }
         } else {
             s = lock_user(VERIFY_WRITE, arg1, len, 0);
             if (!s) {
@@ -317,7 +384,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         GET_ARG(0);
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
-            return env->regs[0];
+            if (env->aarch64) {
+              return env->xregs[0];
+            } else {
+              return env->regs[0];
+            }
         } else {
             return isatty(arg0);
         }
@@ -326,7 +397,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         GET_ARG(1);
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
-            return env->regs[0];
+            if (env->aarch64) {
+              return env->xregs[0];
+            } else {
+              return env->regs[0];
+            }
         } else {
             ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
             if (ret == (uint32_t)-1)
@@ -336,9 +411,16 @@  uint32_t do_arm_semihosting(CPUARMState *env)
     case TARGET_SYS_FLEN:
         GET_ARG(0);
         if (use_gdb_syscalls()) {
+          if (env->aarch64) {
+            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
+                           arg0, env->pc-64);
+            return env->xregs[0];
+
+          } else {
             gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
                            arg0, env->regs[13]-64);
             return env->regs[0];
+          }
         } else {
             struct stat buf;
             ret = set_swi_errno(ts, fstat(arg0, &buf));
@@ -354,7 +436,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         GET_ARG(1);
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
-            ret = env->regs[0];
+            if (env->aarch64) {
+              ret = env->xregs[0];
+            } else {
+              ret = env->regs[0];
+            }
         } else {
             s = lock_user_string(arg0);
             if (!s) {
@@ -373,7 +459,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
                            arg0, (int)arg1+1, arg2, (int)arg3+1);
-            return env->regs[0];
+            if (env->aarch64) {
+              return env->xregs[0];
+            } else {
+              return env->regs[0];
+            }
         } else {
             char *s2;
             s = lock_user_string(arg0);
@@ -398,7 +488,11 @@  uint32_t do_arm_semihosting(CPUARMState *env)
         GET_ARG(1);
         if (use_gdb_syscalls()) {
             gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
-            return env->regs[0];
+            if (env->aarch64) {
+              return env->xregs[0];
+            } else {
+              return env->regs[0];
+            }
         } else {
             s = lock_user_string(arg0);
             if (!s) {
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index d4a5899..2525569 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -56,6 +56,7 @@ 
 #define EXCP_SMC            13   /* Secure Monitor Call */
 #define EXCP_VIRQ           14
 #define EXCP_VFIQ           15
+#define EXCP_ARMV8_HLT      16   /* avoid conflict with cpu-defs.h:EXCP_HLT */
 
 #define ARMV7M_EXCP_RESET   1
 #define ARMV7M_EXCP_NMI     2
@@ -489,7 +490,7 @@  typedef struct CPUARMState {
 
 ARMCPU *cpu_arm_init(const char *cpu_model);
 int cpu_arm_exec(CPUARMState *s);
-uint32_t do_arm_semihosting(CPUARMState *env);
+target_ulong do_arm_semihosting(CPUARMState *env);
 void aarch64_sync_32_to_64(CPUARMState *env);
 void aarch64_sync_64_to_32(CPUARMState *env);
 
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
index 861f6fa..8803293 100644
--- a/target-arm/helper-a64.c
+++ b/target-arm/helper-a64.c
@@ -466,6 +466,14 @@  void aarch64_cpu_do_interrupt(CPUState *cs)
     unsigned int new_el = arm_excp_target_el(cs, cs->exception_index);
     target_ulong addr = env->cp15.vbar_el[new_el];
     unsigned int new_mode = aarch64_pstate_mode(new_el, true);
+#ifndef CONFIG_USER_ONLY
+    uint64_t mask;
+#endif
+
+    uint32_t syndrome =
+      cs->exception_index == EXCP_ARMV8_HLT ?
+        env->exception.syndrome & ~0xffff :
+        env->exception.syndrome;
 
     if (arm_current_el(env) < new_el) {
         if (env->aarch64) {
@@ -482,7 +490,7 @@  void aarch64_cpu_do_interrupt(CPUState *cs)
     if (qemu_loglevel_mask(CPU_LOG_INT)
         && !excp_is_internal(cs->exception_index)) {
         qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%" PRIx32 "\n",
-                      env->exception.syndrome);
+                      syndrome);
     }
 
     if (arm_is_psci_call(cpu, cs->exception_index)) {
@@ -504,8 +512,24 @@  void aarch64_cpu_do_interrupt(CPUState *cs)
     case EXCP_HVC:
     case EXCP_HYP_TRAP:
     case EXCP_SMC:
-        env->cp15.esr_el[new_el] = env->exception.syndrome;
+        env->cp15.esr_el[new_el] = syndrome;
         break;
+#ifndef CONFIG_USER_ONLY
+    case EXCP_ARMV8_HLT:
+      if (env->aarch64) {
+        if (semihosting_enabled) {
+          mask = env->exception.syndrome & 0xffff;
+          if (mask == 0xf000 ) {
+            env->xregs[0] = do_arm_semihosting(env);
+            qemu_log_mask(CPU_LOG_INT, "...handled a semihosting call\n");
+            return;
+          }
+        } else {
+          cpu_abort(cs, "Skipping semihosting call!\n");
+        }
+      }
+      break;
+#endif
     case EXCP_IRQ:
     case EXCP_VIRQ:
         addr += 0x80;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index e4e4931..4491b05 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -12,6 +12,7 @@ 
 #include <zlib.h> /* For crc32 */
 
 #ifndef CONFIG_USER_ONLY
+
 static inline int get_phys_addr(CPUARMState *env, target_ulong address,
                                 int access_type, ARMMMUIdx mmu_idx,
                                 hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 2cc3017..ddbbc0f 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -50,6 +50,7 @@  static const char * const excnames[] = {
     [EXCP_IRQ] = "IRQ",
     [EXCP_FIQ] = "FIQ",
     [EXCP_BKPT] = "Breakpoint",
+    [EXCP_ARMV8_HLT] = "HLT",
     [EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit",
     [EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage",
     [EXCP_STREX] = "QEMU intercept of STREX",
@@ -260,6 +261,13 @@  static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_thumb)
         | (is_thumb ? 0 : ARM_EL_IL);
 }
 
+static inline uint32_t syn_aa64_hlt(uint32_t imm16)
+{
+  // architecturally, the syndrome is uncategorized. We add the imm16 field so
+  // that it can be accessed later for semihosting
+    return (EC_UNCATEGORIZED << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
+}
+
 static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2,
                                            int crn, int crm, int rt,
                                            int isread)
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 0b192a1..14a501c 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -1544,7 +1544,7 @@  static void disas_exc(DisasContext *s, uint32_t insn)
             break;
         }
         /* HLT */
-        unsupported_encoding(s, insn);
+        gen_exception_insn(s, 0, EXCP_ARMV8_HLT, syn_aa64_hlt(imm16));
         break;
     case 5:
         if (op2_ll < 1 || op2_ll > 3) {