diff mbox

[3/4] target-mips: add "-semihosting-arg" option and implement UHI Arg* ops

Message ID 1425056454-27028-4-git-send-email-leon.alrae@imgtec.com
State New
Headers show

Commit Message

Leon Alrae Feb. 27, 2015, 5 p.m. UTC
Add new command line option "-semihosting-arg". It is used for passing input
arguments to the guest in semihosting mode. The option can be used multiple
times. If n arguments are passed, then argument count (semihosting_argc) will
be equal to n+1 as semihosting_argv[0] points at the program name. However, if
no arguments are passed then argument count will be 0.

Also tweak Malta's pseudo-bootloader. On CPU reset the $4 register is set to -1
when semihosting is enabled in order to indicate that the UHI operations should
be used to obtain input arguments.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
---
 hw/mips/mips_malta.c    |  8 +++++++-
 include/sysemu/sysemu.h |  2 ++
 qemu-options.hx         |  8 ++++++++
 target-mips/mips-semi.c | 38 +++++++++++++++++++++++++++++++++++++-
 target-mips/translate.c |  7 +++++++
 vl.c                    | 28 ++++++++++++++++++++++++++++
 6 files changed, 89 insertions(+), 2 deletions(-)

Comments

Matthew Fortune March 1, 2015, 10:39 p.m. UTC | #1
> Add new command line option "-semihosting-arg". It is used for passing
> input arguments to the guest in semihosting mode. The option can be used
> multiple times. If n arguments are passed, then argument count
> (semihosting_argc) will be equal to n+1 as semihosting_argv[0] points at
> the program name. However, if no arguments are passed then argument
> count will be 0.

Is there any specific reason for not passing the name of the executable
even if no -semihosting-arg is present?

I think there are the following cases:

1) one or more -semihosting-arg options are given. So n+1 arguments are
   passed with the first one being kernel/boot name if available. The
   arguments are passed using UHI argc/argn operations (a0 == -1).
2) No -semihosting-arg options are given but the kernel/boot name is known.
   This could be passed either via the normal argument passing conventions
   that are already in place or via argc/argn operations.
3) No -semihosting arg options and unknown application name. For this you
   could again either do what is already done and pass kernel/append
   options or set a0=0 and indicate that there is no information at all.

I wonder if there should be some detection of using both -semihosting-arg
and -append? I think they are in direct conflict. It would probably be
good to have cases 2/3 above do the same as what happened before and
pass kernel name + append options. (By that I also mean setting up the
argv array directly and setting a0 to a positive value (2) rather than
using the new interface).

While it may not be used, this would theoretically allow a kernel to be
booted with -semihosting enabled, pass arguments in the same way they
always have and allow very early boot to emit messages using UHI. I.e.
the kernel will almost certainly never use the argc/argn/argnlen
interface.

I don't have any comments on the code except to say it looks to match
the spec for argc/argn/argnlen.

Thanks,
Matthew
diff mbox

Patch

diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index 5845158..2dfe964 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -634,7 +634,13 @@  static void write_bootloader (CPUMIPSState *env, uint8_t *base,
 
     /* Second part of the bootloader */
     p = (uint32_t *) (base + 0x580);
-    stl_p(p++, 0x24040002);                                      /* addiu a0, zero, 2 */
+
+    if (semihosting_enabled) {
+        /* Preserve a0 content when semihosting is enabled. */
+        stl_p(p++, 0x00000000);                         /* nop */
+    } else {
+        stl_p(p++, 0x24040002);                         /* addiu a0, zero, 2 */
+    }
     stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */
     stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff));        /* ori sp, sp, low(ENVP_ADDR) */
     stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff));       /* lui a1, high(ENVP_ADDR) */
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 1ab7063..7d63da2 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -125,6 +125,8 @@  extern int graphic_rotate;
 extern int no_quit;
 extern int no_shutdown;
 extern int semihosting_enabled;
+extern const char **semihosting_argv;
+extern int semihosting_argc;
 extern int old_param;
 extern int boot_menu;
 extern bool boot_strict;
diff --git a/qemu-options.hx b/qemu-options.hx
index 99ad1ae..bd058d0 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3240,6 +3240,14 @@  Enable semihosting and define where the semihosting calls will be addressed,
 to QEMU (@code{native}) or to GDB (@code{gdb}). The default is @code{auto}, which means
 @code{gdb} during debug sessions and @code{native} otherwise (ARM, M68K, Xtensa only).
 ETEXI
+DEF("semihosting-arg", HAS_ARG, QEMU_OPTION_semihosting_arg,
+    "-semihosting-arg    arguments passed to the guest program\n",
+    QEMU_ARCH_MIPS)
+STEXI
+@item -semihosting-arg
+@findex -semihosting-arg
+Arguments passed to the guest program (MIPS only).
+ETEXI
 DEF("old-param", 0, QEMU_OPTION_old_param,
     "-old-param      old param mode\n", QEMU_ARCH_ARM)
 STEXI
diff --git a/target-mips/mips-semi.c b/target-mips/mips-semi.c
index 3bf7b2a..63f2700 100644
--- a/target-mips/mips-semi.c
+++ b/target-mips/mips-semi.c
@@ -21,6 +21,9 @@ 
 #include "cpu.h"
 #include "exec/helper-proto.h"
 #include "exec/softmmu-semi.h"
+#ifndef CONFIG_USER_ONLY
+#include "sysemu/sysemu.h"
+#endif
 
 typedef enum UHIOp {
     UHI_exit = 1,
@@ -71,6 +74,12 @@  enum UHIOpenFlags {
     UHIOpen_EXCL   = 0x800
 };
 
+#ifdef CONFIG_USER_ONLY
+/* Suppress compiler errors in linux-user. */
+static const char **semihosting_argv;
+static int semihosting_argc;
+#endif
+
 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
                                 target_ulong vaddr)
 {
@@ -169,6 +178,21 @@  static int read_from_file(CPUMIPSState *env, target_ulong fd,
     return num_of_bytes;
 }
 
+static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
+                               target_ulong vaddr)
+{
+    int strsize = strlen(semihosting_argv[arg_num]) + 1;
+    char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
+    if (!dst) {
+        return -1;
+    }
+
+    strcpy(dst, semihosting_argv[arg_num]);
+
+    unlock_user(dst, vaddr, strsize);
+    return 0;
+}
+
 #define GET_TARGET_STRING(p, addr)              \
     do {                                        \
         p = lock_user_string(addr);             \
@@ -248,9 +272,21 @@  void helper_do_semihosting(CPUMIPSState *env)
         }
         break;
     case UHI_argc:
+        gpr[2] = semihosting_argc;
+        break;
     case UHI_argnlen:
+        if (gpr[4] >= semihosting_argc) {
+            gpr[2] = -1;
+            goto uhi_done;
+        }
+        gpr[2] = strlen(semihosting_argv[gpr[4]]);
+        break;
     case UHI_argn:
-        /* TODO */
+        if (gpr[4] >= semihosting_argc) {
+            gpr[2] = -1;
+            goto uhi_done;
+        }
+        gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
         break;
     case UHI_plog:
         GET_TARGET_STRING(p, gpr[4]);
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 82fa5a4..678c3d5 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -19652,6 +19652,13 @@  void cpu_state_reset(CPUMIPSState *env)
     restore_rounding_mode(env);
     restore_flush_mode(env);
     cs->exception_index = EXCP_NONE;
+
+#ifndef CONFIG_USER_ONLY
+    if (semihosting_enabled) {
+        /* When $4 is -1 the UHI interface will be used for argc and argv */
+        env->active_tc.gpr[4] = -1;
+    }
+#endif
 }
 
 void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, int pc_pos)
diff --git a/vl.c b/vl.c
index e1ffd0a..b2d3422 100644
--- a/vl.c
+++ b/vl.c
@@ -169,6 +169,8 @@  const char *watchdog;
 QEMUOptionRom option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
 int semihosting_enabled = 0;
+const char **semihosting_argv;
+int semihosting_argc;
 int old_param = 0;
 const char *qemu_name;
 int alt_grab = 0;
@@ -3560,6 +3562,22 @@  int main(int argc, char **argv, char **envp)
                     exit(1);
                 }
                 break;
+            case QEMU_OPTION_semihosting_arg:
+                if (semihosting_argc == 0) {
+                    /* If arguments are present then the first argument goes to
+                       argv[1] as argv[0] is reserved for the program name */
+                    semihosting_argc = 2;
+                    semihosting_argv =
+                        g_malloc(semihosting_argc * sizeof(void *));
+                    semihosting_argv[1] = optarg;
+                } else {
+                    semihosting_argc++;
+                    semihosting_argv =
+                        g_realloc(semihosting_argv,
+                                  semihosting_argc * sizeof(void *));
+                    semihosting_argv[semihosting_argc - 1] = optarg;
+                }
+                break;
             case QEMU_OPTION_tdf:
                 fprintf(stderr, "Warning: user space PIT time drift fix "
                                 "is no longer supported.\n");
@@ -4078,6 +4096,16 @@  int main(int argc, char **argv, char **envp)
         current_machine->kernel_cmdline = (char *)kernel_cmdline;
     }
 
+    if (semihosting_argc) {
+        if (kernel_filename) {
+            semihosting_argv[0] = kernel_filename;
+        } else if (bios_name) {
+            semihosting_argv[0] = bios_name;
+        } else {
+            semihosting_argv[0] = "";
+        }
+    }
+
     linux_boot = (kernel_filename != NULL);
 
     if (!linux_boot && *kernel_cmdline != '\0') {