diff mbox

[v4,2/2] semihosting: add --semihosting-config arg sub-argument

Message ID 1432656234-9791-3-git-send-email-leon.alrae@imgtec.com
State New
Headers show

Commit Message

Leon Alrae May 26, 2015, 4:03 p.m. UTC
Add new "arg" sub-argument to the --semihosting-config allowing the user
to pass multiple input arguments separately. It is required for example
by UHI semihosting to construct argc and argv.

Also, update ARM semihosting to support new option (at the moment it is
the only target which cares about arguments).

If the semihosting is enabled and no semihosting args have been specified,
then fall back to -kernel/-append. The -append string is split on whitespace
before initializing semihosting.argv[1..n]; this is different from what
QEMU MIPS machines' pseudo-bootloaders do (i.e. argv[1] contains the whole
-append), but is more intuitive from UHI user's point of view and Linux
kernel just does not care as it concatenates argv[1..n] into single cmdline
string anyway.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
---
 include/exec/semihost.h | 18 ++++++++++++++
 qemu-options.hx         | 21 ++++++++++++----
 target-arm/arm-semi.c   | 10 +++-----
 vl.c                    | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+), 12 deletions(-)

Comments

Peter Maydell June 5, 2015, 3:23 p.m. UTC | #1
On 26 May 2015 at 17:03, Leon Alrae <leon.alrae@imgtec.com> wrote:
> Add new "arg" sub-argument to the --semihosting-config allowing the user
> to pass multiple input arguments separately. It is required for example
> by UHI semihosting to construct argc and argv.
>
> Also, update ARM semihosting to support new option (at the moment it is
> the only target which cares about arguments).
>
> If the semihosting is enabled and no semihosting args have been specified,
> then fall back to -kernel/-append. The -append string is split on whitespace
> before initializing semihosting.argv[1..n]; this is different from what
> QEMU MIPS machines' pseudo-bootloaders do (i.e. argv[1] contains the whole
> -append), but is more intuitive from UHI user's point of view and Linux
> kernel just does not care as it concatenates argv[1..n] into single cmdline
> string anyway.
>
> Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>

> --- a/target-arm/arm-semi.c
> +++ b/target-arm/arm-semi.c
> @@ -27,6 +27,7 @@
>  #include <time.h>
>
>  #include "cpu.h"
> +#include "exec/semihost.h"
>  #ifdef CONFIG_USER_ONLY
>  #include "qemu.h"
>
> @@ -440,10 +441,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
>              input_size = arg1;
>              /* Compute the size of the output string.  */
>  #if !defined(CONFIG_USER_ONLY)
> -            output_size = strlen(ts->boot_info->kernel_filename)
> -                        + 1  /* Separating space.  */
> -                        + strlen(ts->boot_info->kernel_cmdline)
> -                        + 1; /* Terminating null byte.  */
> +            output_size = strlen(semihosting_get_cmdline()) + 1;

It looks like semihosting_get_cmdline() can return NULL,
in which case this will blow up, I think.

Patch looks OK otherwise (haven't tested it yet).

-- PMM
Leon Alrae June 5, 2015, 8:09 p.m. UTC | #2
On 05/06/15 16:23, Peter Maydell wrote:
> On 26 May 2015 at 17:03, Leon Alrae <leon.alrae@imgtec.com> wrote:
>> --- a/target-arm/arm-semi.c
>> +++ b/target-arm/arm-semi.c
>> @@ -27,6 +27,7 @@
>>  #include <time.h>
>>
>>  #include "cpu.h"
>> +#include "exec/semihost.h"
>>  #ifdef CONFIG_USER_ONLY
>>  #include "qemu.h"
>>
>> @@ -440,10 +441,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
>>              input_size = arg1;
>>              /* Compute the size of the output string.  */
>>  #if !defined(CONFIG_USER_ONLY)
>> -            output_size = strlen(ts->boot_info->kernel_filename)
>> -                        + 1  /* Separating space.  */
>> -                        + strlen(ts->boot_info->kernel_cmdline)
>> -                        + 1; /* Terminating null byte.  */
>> +            output_size = strlen(semihosting_get_cmdline()) + 1;
> 
> It looks like semihosting_get_cmdline() can return NULL,
> in which case this will blow up, I think.

semihosting_get_cmdline() returns NULL if neither semihosting args nor
-kernel have been specified. As far as I can tell existing
implementation may also blow up if kernel_filename is NULL, so we retain
the same behaviour. Besides, it's not clear to me how the
TARGET_SYS_GET_CMDLINE should behave if cmdline is not available,
whether should return -1 or pass an empty string to the guest. For me
this looks like a separate issue, not much related to this patch series.

Thanks,
Leon
Liviu Ionescu June 5, 2015, 9:11 p.m. UTC | #3
> On 05 Jun 2015, at 23:09, Leon Alrae <leon.alrae@imgtec.com> wrote:
> 
> ... As far as I can tell existing
> implementation may also blow up if kernel_filename is NULL,

not necessarily, in my branch it is perfectly legal to start qemu without an image, as long as the gdb server is started, since the application will be loaded by the gdb client. (this is how the Eclipse plug-in is configured to start qemu).

> ... how the
> TARGET_SYS_GET_CMDLINE should behave if cmdline is not available,
> whether should return -1 or pass an empty string to the guest. 

for consistency I would suggest to return -1 for all cases that do not return a legal cmdline.

regards,

Liviu
Peter Maydell June 5, 2015, 10:54 p.m. UTC | #4
On 5 June 2015 at 22:11, Liviu Ionescu <ilg@livius.net> wrote:
>
>> On 05 Jun 2015, at 23:09, Leon Alrae <leon.alrae@imgtec.com> wrote:
>> ... how the
>> TARGET_SYS_GET_CMDLINE should behave if cmdline is not available,
>> whether should return -1 or pass an empty string to the guest.
>
> for consistency I would suggest to return -1 for all cases that do
> not return a legal cmdline.

The existing linux-user implementation of this semihosting
call handles this case by returning the empty string, so
consistency suggests following that in the equivalent
softmmu case.

-- PMM
Liviu Ionescu June 6, 2015, 8:50 a.m. UTC | #5
> On 06 Jun 2015, at 01:54, Peter Maydell <peter.maydell@linaro.org> wrote:
> 
> On 5 June 2015 at 22:11, Liviu Ionescu <ilg@livius.net> wrote:
>> 
>>> On 05 Jun 2015, at 23:09, Leon Alrae <leon.alrae@imgtec.com> wrote:
>>> ... how the
>>> TARGET_SYS_GET_CMDLINE should behave if cmdline is not available,
>>> whether should return -1 or pass an empty string to the guest.
>> 
>> for consistency I would suggest to return -1 for all cases that do
>> not return a legal cmdline.
> 
> The existing linux-user implementation of this semihosting
> call handles this case by returning the empty string, so
> consistency suggests following that in the equivalent
> softmmu case.

in this case it doesn't make any major difference, the application should accommodate both cases, but, as a general comment, if one case is broken/poorly implemented, for the sake of consistency I would not rush to make all others broken too, but I would first try to find a solution to fix/improve them all.

regards,

Liviu
Liviu Ionescu June 16, 2015, 12:32 p.m. UTC | #6
would it be possible to have all the semihosting patches ready for 2.4?

regards,

Liviu
Peter Maydell June 16, 2015, 2:03 p.m. UTC | #7
On 16 June 2015 at 13:32, Liviu Ionescu <ilg@livius.net> wrote:
> would it be possible to have all the semihosting patches ready for 2.4?

Yes, I agree that would be good. Is it just this 2 patch
series, or are there others too?

thanks
-- PMM
Leon Alrae June 16, 2015, 2:20 p.m. UTC | #8
On 16/06/2015 15:03, Peter Maydell wrote:
> On 16 June 2015 at 13:32, Liviu Ionescu <ilg@livius.net> wrote:
>> would it be possible to have all the semihosting patches ready for 2.4?
> 
> Yes, I agree that would be good. Is it just this 2 patch
> series, or are there others too?

Just to confirm -- the only thing required before applying this 2 patch series
is testing in ARM context by you, right?

Remaining semihosting patches I've got are MIPS specific, but they can go via
target-mips tree separately.

Leon
diff mbox

Patch

diff --git a/include/exec/semihost.h b/include/exec/semihost.h
index c2f0bcb..5980939 100644
--- a/include/exec/semihost.h
+++ b/include/exec/semihost.h
@@ -36,9 +36,27 @@  static inline SemihostingTarget semihosting_get_target(void)
 {
     return SEMIHOSTING_TARGET_AUTO;
 }
+
+static inline const char *semihosting_get_arg(int i)
+{
+    return NULL;
+}
+
+static inline int semihosting_get_argc(void)
+{
+    return 0;
+}
+
+static inline const char *semihosting_get_cmdline(void)
+{
+    return NULL;
+}
 #else
 bool semihosting_enabled(void);
 SemihostingTarget semihosting_get_target(void);
+const char *semihosting_get_arg(int i);
+int semihosting_get_argc(void);
+const char *semihosting_get_cmdline(void);
 #endif
 
 #endif
diff --git a/qemu-options.hx b/qemu-options.hx
index ec356f6..90500ad 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3296,14 +3296,25 @@  STEXI
 Enable semihosting mode (ARM, M68K, Xtensa only).
 ETEXI
 DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config,
-    "-semihosting-config [enable=on|off,]target=native|gdb|auto   semihosting configuration\n",
+    "-semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]\n" \
+    "                semihosting configuration\n",
 QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
 STEXI
-@item -semihosting-config [enable=on|off,]target=native|gdb|auto
+@item -semihosting-config [enable=on|off][,target=native|gdb|auto][,arg=str[,...]]
 @findex -semihosting-config
-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).
+Enable and configure semihosting (ARM, M68K, Xtensa only).
+@table @option
+@item target=@code{native|gdb|auto}
+Defines 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.
+@item arg=@var{str1},arg=@var{str2},...
+Allows the user to pass input arguments, and can be used multiple times to build
+up a list. The old-style @code{-kernel}/@code{-append} method of passing a
+command line is still supported for backward compatibility. If both the
+@code{--semihosting-config arg} and the @code{-kernel}/@code{-append} are
+specified, the former is passed to semihosting as it always takes precedence.
+@end table
 ETEXI
 DEF("old-param", 0, QEMU_OPTION_old_param,
     "-old-param      old param mode\n", QEMU_ARCH_ARM)
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index a8b83e6..74a67e9 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -27,6 +27,7 @@ 
 #include <time.h>
 
 #include "cpu.h"
+#include "exec/semihost.h"
 #ifdef CONFIG_USER_ONLY
 #include "qemu.h"
 
@@ -440,10 +441,7 @@  uint32_t do_arm_semihosting(CPUARMState *env)
             input_size = arg1;
             /* Compute the size of the output string.  */
 #if !defined(CONFIG_USER_ONLY)
-            output_size = strlen(ts->boot_info->kernel_filename)
-                        + 1  /* Separating space.  */
-                        + strlen(ts->boot_info->kernel_cmdline)
-                        + 1; /* Terminating null byte.  */
+            output_size = strlen(semihosting_get_cmdline()) + 1;
 #else
             unsigned int i;
 
@@ -474,9 +472,7 @@  uint32_t do_arm_semihosting(CPUARMState *env)
 
             /* Copy the command-line arguments.  */
 #if !defined(CONFIG_USER_ONLY)
-            pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
-            pstrcat(output_buffer, output_size, " ");
-            pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
+            pstrcpy(output_buffer, output_size, semihosting_get_cmdline());
 #else
             if (output_size == 1) {
                 /* Empty command-line.  */
diff --git a/vl.c b/vl.c
index f3319a9..d599411 100644
--- a/vl.c
+++ b/vl.c
@@ -486,6 +486,9 @@  static QemuOptsList qemu_semihosting_config_opts = {
         }, {
             .name = "target",
             .type = QEMU_OPT_STRING,
+        }, {
+            .name = "arg",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -1230,6 +1233,9 @@  static void configure_msg(QemuOpts *opts)
 typedef struct SemihostingConfig {
     bool enabled;
     SemihostingTarget target;
+    const char **argv;
+    int argc;
+    const char *cmdline; /* concatenated argv */
 } SemihostingConfig;
 
 static SemihostingConfig semihosting;
@@ -1244,6 +1250,56 @@  SemihostingTarget semihosting_get_target(void)
     return semihosting.target;
 }
 
+const char *semihosting_get_arg(int i)
+{
+    if (i >= semihosting.argc) {
+        return NULL;
+    }
+    return semihosting.argv[i];
+}
+
+int semihosting_get_argc(void)
+{
+    return semihosting.argc;
+}
+
+const char *semihosting_get_cmdline(void)
+{
+    if (semihosting.cmdline == NULL && semihosting.argc > 0) {
+        semihosting.cmdline = g_strjoinv(" ", (gchar **)semihosting.argv);
+    }
+    return semihosting.cmdline;
+}
+
+static int add_semihosting_arg(const char *name, const char *val, void *opaque)
+{
+    SemihostingConfig *s = opaque;
+    if (strcmp(name, "arg") == 0) {
+        s->argc++;
+        /* one extra element as g_strjoinv() expects NULL-terminated array */
+        s->argv = g_realloc(s->argv, (s->argc + 1) * sizeof(void *));
+        s->argv[s->argc - 1] = val;
+        s->argv[s->argc] = NULL;
+    }
+    return 0;
+}
+
+/* Use strings passed via -kernel/-append to initialize semihosting.argv[] */
+static inline void semihosting_arg_fallback(const char *file, const char *cmd)
+{
+    char *cmd_token;
+
+    /* argv[0] */
+    add_semihosting_arg("arg", file, &semihosting);
+
+    /* split -append and initialize argv[1..n] */
+    cmd_token = strtok(g_strdup(cmd), " ");
+    while (cmd_token) {
+        add_semihosting_arg("arg", cmd_token, &semihosting);
+        cmd_token = strtok(NULL, " ");
+    }
+}
+
 /***********************************************************/
 /* USB devices */
 
@@ -3592,6 +3648,9 @@  int main(int argc, char **argv, char **envp)
                     } else {
                         semihosting.target = SEMIHOSTING_TARGET_AUTO;
                     }
+                    /* Set semihosting argument count and vector */
+                    qemu_opt_foreach(opts, add_semihosting_arg,
+                                     &semihosting, 0);
                 } else {
                     fprintf(stderr, "Unsupported semihosting-config %s\n",
                             optarg);
@@ -4150,6 +4209,11 @@  int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
+    if (semihosting_enabled() && !semihosting_get_argc() && kernel_filename) {
+        /* fall back to the -kernel/-append */
+        semihosting_arg_fallback(kernel_filename, kernel_cmdline);
+    }
+
     os_set_line_buffering();
 
 #ifdef CONFIG_SPICE