diff mbox series

[v2,03/28] semihosting: implement a semihosting console

Message ID 20190523102532.10486-4-alex.bennee@linaro.org
State New
Headers show
Series current testing/next queue | expand

Commit Message

Alex Bennée May 23, 2019, 10:25 a.m. UTC
This provides two functions for handling console output that handle
the common backend behaviour for semihosting.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

---
 gdbstub.c                        |  5 +++
 hw/semihosting/Makefile.objs     |  1 +
 hw/semihosting/console.c         | 70 ++++++++++++++++++++++++++++++++
 include/exec/gdbstub.h           | 11 +++++
 include/hw/semihosting/console.h | 38 +++++++++++++++++
 5 files changed, 125 insertions(+)
 create mode 100644 hw/semihosting/console.c
 create mode 100644 include/hw/semihosting/console.h

-- 
2.20.1

Comments

Peter Maydell May 23, 2019, 1:13 p.m. UTC | #1
On Thu, 23 May 2019 at 11:39, Alex Bennée <alex.bennee@linaro.org> wrote:
>

> This provides two functions for handling console output that handle

> the common backend behaviour for semihosting.

>

> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>


> diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h

> index 08363969c14..b2963547c48 100644

> --- a/include/exec/gdbstub.h

> +++ b/include/exec/gdbstub.h

> @@ -44,6 +44,17 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...);

>   * argument list.

>   */

>  void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va);

> +/**

> + * gdb_do_console_out:

> + * @gs: guest address of string to send

> + * @len: length of string

> + *

> + * Sends a string to gdb console. Unlike the system call interface

> + * there is no callback and we assume the system call always

> + * succeeds.

> + */

> +void gdb_do_console_out(target_ulong s, int len);


I'm not sure about the "no callback" part of this API. The operation
here is genuinely asynchronous and providing no mechanism for the
caller to be able to say "ok, now wait til it completes" doesn't
seem like a good plan.

thanks
-- PMM
Alex Bennée May 24, 2019, 10:46 a.m. UTC | #2
Peter Maydell <peter.maydell@linaro.org> writes:

> On Thu, 23 May 2019 at 11:39, Alex Bennée <alex.bennee@linaro.org> wrote:

>>

>> This provides two functions for handling console output that handle

>> the common backend behaviour for semihosting.

>>

>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

>

>> diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h

>> index 08363969c14..b2963547c48 100644

>> --- a/include/exec/gdbstub.h

>> +++ b/include/exec/gdbstub.h

>> @@ -44,6 +44,17 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...);

>>   * argument list.

>>   */

>>  void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va);

>> +/**

>> + * gdb_do_console_out:

>> + * @gs: guest address of string to send

>> + * @len: length of string

>> + *

>> + * Sends a string to gdb console. Unlike the system call interface

>> + * there is no callback and we assume the system call always

>> + * succeeds.

>> + */

>> +void gdb_do_console_out(target_ulong s, int len);

>

> I'm not sure about the "no callback" part of this API. The operation

> here is genuinely asynchronous and providing no mechanism for the

> caller to be able to say "ok, now wait til it completes" doesn't

> seem like a good plan.


Well the caller doesn't really get a choice. At least in system mode
gdbstub does a vm_stop(RUN_STATE_DEBUG) and brings everything to a halt
anyway. All we've removed is the ability to tack on a callback (which
can get run in all sorts of contexts) when we restart.

I could just drop the API here and allow the semihosting API to call
gdb_do_syscallv directly?

--
Alex Bennée
Peter Maydell May 24, 2019, 10:56 a.m. UTC | #3
On Fri, 24 May 2019 at 11:46, Alex Bennée <alex.bennee@linaro.org> wrote:
>

>

> Peter Maydell <peter.maydell@linaro.org> writes:

>

> > On Thu, 23 May 2019 at 11:39, Alex Bennée <alex.bennee@linaro.org> wrote:

> > I'm not sure about the "no callback" part of this API. The operation

> > here is genuinely asynchronous and providing no mechanism for the

> > caller to be able to say "ok, now wait til it completes" doesn't

> > seem like a good plan.

>

> Well the caller doesn't really get a choice. At least in system mode

> gdbstub does a vm_stop(RUN_STATE_DEBUG) and brings everything to a halt

> anyway. All we've removed is the ability to tack on a callback (which

> can get run in all sorts of contexts) when we restart.


gdb_do_syscall() is asynchronous -- it arranges for the syscall
to happen, but makes no guarantee about when it will finish.
At the moment the gdb_do_syscall() API allows the caller
to cope with this asynchronousness, because when the callback
is called the syscall has definitely finished. As it happens
the callers are buggy in that they don't actually do the
sync that they need to do, but we could fix that bug (ie post
a semaphore in the callback function, and wait on it after
the gdb_do_syscall() call). The API for this new function
doesn't allow us to do that.

> I could just drop the API here and allow the semihosting API to call

> gdb_do_syscallv directly?


I think we should either make the implementation of the function
properly synchronous (ie do the post-semaphore-in-callback,
wait-on-it-after-gdb_do_syscallv thing in the implementation,
or have an API that lets callers do it.

Perhaps we should just make gdb_do_syscall really be
synchronous (ie have it do the semaphore stuff)? We
only actually use it in semihosting, and I think all
those cases require that the operation completes before
we can resume execution of the guest CPU. So doing the
synchronization in one place in the gdb code would be
simpler than doing it separately in all the callers...

thanks
-- PMM
Alex Bennée May 24, 2019, 11:25 a.m. UTC | #4
Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 24 May 2019 at 11:46, Alex Bennée <alex.bennee@linaro.org> wrote:

>>

>>

>> Peter Maydell <peter.maydell@linaro.org> writes:

>>

>> > On Thu, 23 May 2019 at 11:39, Alex Bennée <alex.bennee@linaro.org> wrote:

>> > I'm not sure about the "no callback" part of this API. The operation

>> > here is genuinely asynchronous and providing no mechanism for the

>> > caller to be able to say "ok, now wait til it completes" doesn't

>> > seem like a good plan.

>>

>> Well the caller doesn't really get a choice. At least in system mode

>> gdbstub does a vm_stop(RUN_STATE_DEBUG) and brings everything to a halt

>> anyway. All we've removed is the ability to tack on a callback (which

>> can get run in all sorts of contexts) when we restart.

>

> gdb_do_syscall() is asynchronous -- it arranges for the syscall

> to happen, but makes no guarantee about when it will finish.

> At the moment the gdb_do_syscall() API allows the caller

> to cope with this asynchronousness, because when the callback

> is called the syscall has definitely finished. As it happens

> the callers are buggy in that they don't actually do the

> sync that they need to do, but we could fix that bug (ie post

> a semaphore in the callback function, and wait on it after

> the gdb_do_syscall() call). The API for this new function

> doesn't allow us to do that.


So for the ARM semi side the console writes are treated as always
successful so the return value is correct (it doesn't matter for writec)
and no syncing to the guest is required. However I ran into a problem
because in gdbstub we have:

    /* Is there a GDB syscall waiting to be sent?  */
    if (s->current_syscall_cb) {
        put_packet(s, s->syscall_buf);
        return;
    }

which means it can't accept NULL for the callback. So I've removed the
gdb_console_out and done:

  static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err)
  {
      if (ret == (target_ulong) -1) {
          qemu_log("%s: gdb console output failed (%s)", __func__, strerror(-err));
      }
  }

  int qemu_semihosting_console_out(CPUArchState *env, target_ulong addr, int len)
  {
      GString *s = copy_user_string(env, addr, len);
      int out = s->len;

      if (use_gdb_syscalls()) {
          gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, s->len);
      } else {
          out = qemu_semihosting_log_out(s->str, s->len);
      }

      g_string_free(s, true);
      return out;
  }

for now.

>> I could just drop the API here and allow the semihosting API to call

>> gdb_do_syscallv directly?

>

> I think we should either make the implementation of the function

> properly synchronous (ie do the post-semaphore-in-callback,

> wait-on-it-after-gdb_do_syscallv thing in the implementation,

> or have an API that lets callers do it.

>

> Perhaps we should just make gdb_do_syscall really be

> synchronous (ie have it do the semaphore stuff)? We

> only actually use it in semihosting, and I think all

> those cases require that the operation completes before

> we can resume execution of the guest CPU. So doing the

> synchronization in one place in the gdb code would be

> simpler than doing it separately in all the callers...


Maybe.. this seems like a bigger clean-up to do that properly. Maybe
that would be worth tackling after the gdbstub refactor stuff goes in?

--
Alex Bennée
diff mbox series

Patch

diff --git a/gdbstub.c b/gdbstub.c
index 793218bb43a..b4334014373 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1987,6 +1987,11 @@  void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
     va_end(va);
 }
 
+void gdb_do_console_out(target_ulong s, int len)
+{
+    gdb_do_syscall(NULL, "write,2,%x,1", s, len);
+}
+
 static void gdb_read_byte(GDBState *s, int ch)
 {
     uint8_t reply;
diff --git a/hw/semihosting/Makefile.objs b/hw/semihosting/Makefile.objs
index 09c19bf19ed..4ad47c05c06 100644
--- a/hw/semihosting/Makefile.objs
+++ b/hw/semihosting/Makefile.objs
@@ -1 +1,2 @@ 
 obj-$(CONFIG_SEMIHOSTING) += config.o
+obj-$(CONFIG_SEMIHOSTING) += console.o
diff --git a/hw/semihosting/console.c b/hw/semihosting/console.c
new file mode 100644
index 00000000000..ad6f67ecc71
--- /dev/null
+++ b/hw/semihosting/console.c
@@ -0,0 +1,70 @@ 
+/*
+ * Semihosting Console Support
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * This provides support for outputting to a semihosting console.
+ *
+ * While most semihosting implementations support reading and writing
+ * to arbitrary file descriptors we treat the console as something
+ * specifically for debugging interaction. This means messages can be
+ * re-directed to gdb (if currently being used to debug) or even
+ * re-directed elsewhere.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/semihosting/console.h"
+#include "exec/gdbstub.h"
+#include "qemu/log.h"
+
+int qemu_semihosting_log_out(const char *s, int len)
+{
+    return write(STDERR_FILENO, s, len);
+}
+
+/*
+ * A re-implementation of lock_user_string that we can use locally
+ * instead of relying on softmmu-semi. Hopefully we can deprecate that
+ * in time. We either copy len bytes if specified or until we find a NULL.
+ */
+static GString *copy_user_string(CPUArchState *env, target_ulong addr, int len)
+{
+    CPUState *cpu = ENV_GET_CPU(env);
+    GString *s = g_string_sized_new(len ? len : 128);
+    uint8_t c;
+    bool done;
+
+    do {
+        if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) {
+            s = g_string_append_c(s, c);
+            done = len ? s->len == len : c == 0;
+        } else {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: passed inaccessible address " TARGET_FMT_lx,
+                          __func__, addr);
+            done = true;
+        }
+    } while (!done);
+
+    return s;
+}
+
+
+int qemu_semihosting_console_out(CPUArchState *env, target_ulong addr, int len)
+{
+    GString *s = copy_user_string(env, addr, len);
+    int out = s->len;
+
+    if (use_gdb_syscalls()) {
+        gdb_do_console_out(addr, s->len);
+    } else {
+        out = qemu_semihosting_log_out(s->str, s->len);
+    }
+
+    g_string_free(s, true);
+    return out;
+}
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index 08363969c14..b2963547c48 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -44,6 +44,17 @@  void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...);
  * argument list.
  */
 void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va);
+/**
+ * gdb_do_console_out:
+ * @gs: guest address of string to send
+ * @len: length of string
+ *
+ * Sends a string to gdb console. Unlike the system call interface
+ * there is no callback and we assume the system call always
+ * succeeds.
+ */
+void gdb_do_console_out(target_ulong s, int len);
+
 int use_gdb_syscalls(void);
 void gdb_set_stop_cpu(CPUState *cpu);
 void gdb_exit(CPUArchState *, int);
diff --git a/include/hw/semihosting/console.h b/include/hw/semihosting/console.h
new file mode 100644
index 00000000000..30e66ae20aa
--- /dev/null
+++ b/include/hw/semihosting/console.h
@@ -0,0 +1,38 @@ 
+/*
+ * Semihosting Console
+ *
+ * Copyright (c) 2019 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _SEMIHOST_CONSOLE_H_
+#define _SEMIHOST_CONSOLE_H_
+
+/**
+ * qemu_semihosting_console_out:
+ * @env: CPUArchState
+ * @s: host address of guest string
+ * @len: length of string or 0 (string is null terminated)
+ *
+ * Send a guest string to the debug console. This may be the remote
+ * gdb session if a softmmu guest is currently being debugged.
+ *
+ * Returns: number of bytes written.
+ */
+int qemu_semihosting_console_out(CPUArchState *env, target_ulong s, int len);
+
+/**
+ * qemu_semihosting_log_out:
+ * @s: pointer to string
+ * @len: length of string
+ *
+ * Send a string to the debug output. Unlike console_out these strings
+ * can't be sent to a remote gdb instance as they don't exist in guest
+ * memory.
+ *
+ * Returns: number of bytes written
+ */
+int qemu_semihosting_log_out(const char *s, int len);
+
+#endif /* _SEMIHOST_CONSOLE_H_ */