diff mbox series

[v4,43/53] semihosting: Expand qemu_semihosting_console_inc to read

Message ID 20220607204557.658541-44-richard.henderson@linaro.org
State Accepted
Commit e7fb6f320548c1b0c25d291466a0249ee80d91b6
Headers show
Series semihosting cleanup | expand

Commit Message

Richard Henderson June 7, 2022, 8:45 p.m. UTC
Allow more than one character to be read at one time.
Will be used by m68k and nios2 semihosting for stdio.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 include/semihosting/console.h | 12 +++++++-----
 linux-user/semihost.c         | 10 ++++++----
 semihosting/arm-compat-semi.c | 11 +++++++++--
 semihosting/console.c         | 16 ++++++++++++----
 4 files changed, 34 insertions(+), 15 deletions(-)

Comments

Luc Michel June 24, 2022, 9:03 a.m. UTC | #1
On 13:45 Tue 07 Jun     , Richard Henderson wrote:
> Allow more than one character to be read at one time.
> Will be used by m68k and nios2 semihosting for stdio.
> 
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

Reviewed-by: Luc Michel <lmichel@kalray.eu>

> ---
>  include/semihosting/console.h | 12 +++++++-----
>  linux-user/semihost.c         | 10 ++++++----
>  semihosting/arm-compat-semi.c | 11 +++++++++--
>  semihosting/console.c         | 16 ++++++++++++----
>  4 files changed, 34 insertions(+), 15 deletions(-)
> 
> diff --git a/include/semihosting/console.h b/include/semihosting/console.h
> index 27f8e9ae2e..39dbf1b062 100644
> --- a/include/semihosting/console.h
> +++ b/include/semihosting/console.h
> @@ -38,19 +38,21 @@ int qemu_semihosting_console_outs(CPUArchState *env, target_ulong s);
>  void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c);
>  
>  /**
> - * qemu_semihosting_console_inc:
> + * qemu_semihosting_console_read:
>   * @cs: CPUState
> + * @buf: host buffer
> + * @len: buffer size
>   *
> - * Receive single character from debug console.  As this call may block
> - * if no data is available we suspend the CPU and will re-execute the
> + * Receive at least one character from debug console.  As this call may
> + * block if no data is available we suspend the CPU and will re-execute the
>   * instruction when data is there. Therefore two conditions must be met:
>   *
>   *   - CPUState is synchronized before calling this function
>   *   - pc is only updated once the character is successfully returned
>   *
> - * Returns: character read OR cpu_loop_exit!
> + * Returns: number of characters read, OR cpu_loop_exit!
>   */
> -target_ulong qemu_semihosting_console_inc(CPUState *cs);
> +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len);
>  
>  /**
>   * qemu_semihosting_log_out:
> diff --git a/linux-user/semihost.c b/linux-user/semihost.c
> index f14c6ae21d..2029fb674c 100644
> --- a/linux-user/semihost.c
> +++ b/linux-user/semihost.c
> @@ -56,21 +56,23 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr)
>   * program is expecting more normal behaviour. This is slow but
>   * nothing using semihosting console reading is expecting to be fast.
>   */
> -target_ulong qemu_semihosting_console_inc(CPUState *cs)
> +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
>  {
> -    uint8_t c;
> +    int ret;
>      struct termios old_tio, new_tio;
>  
>      /* Disable line-buffering and echo */
>      tcgetattr(STDIN_FILENO, &old_tio);
>      new_tio = old_tio;
>      new_tio.c_lflag &= (~ICANON & ~ECHO);
> +    new_tio.c_cc[VMIN] = 1;
> +    new_tio.c_cc[VTIME] = 0;
>      tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
>  
> -    c = getchar();
> +    ret = fread(buf, 1, len, stdin);
>  
>      /* restore config */
>      tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
>  
> -    return (target_ulong) c;
> +    return ret;
>  }
> diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c
> index e7de52d042..20e99cdcc0 100644
> --- a/semihosting/arm-compat-semi.c
> +++ b/semihosting/arm-compat-semi.c
> @@ -427,8 +427,15 @@ void do_common_semihosting(CPUState *cs)
>          break;
>  
>      case TARGET_SYS_READC:
> -        ret = qemu_semihosting_console_inc(cs);
> -        common_semi_set_ret(cs, ret);
> +        {
> +            uint8_t ch;
> +            int ret = qemu_semihosting_console_read(cs, &ch, 1);
> +            if (ret == 1) {
> +                common_semi_cb(cs, ch, 0);
> +            } else {
> +                common_semi_cb(cs, -1, EIO);
> +            }
> +        }
>          break;
>  
>      case TARGET_SYS_ISERROR:
> diff --git a/semihosting/console.c b/semihosting/console.c
> index 7b896fe43b..df618a28a4 100644
> --- a/semihosting/console.c
> +++ b/semihosting/console.c
> @@ -144,12 +144,14 @@ static void console_read(void *opaque, const uint8_t *buf, int size)
>      c->sleeping_cpus = NULL;
>  }
>  
> -target_ulong qemu_semihosting_console_inc(CPUState *cs)
> +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
>  {
> -    uint8_t ch;
>      SemihostingConsole *c = &console;
> +    int ret = 0;
>  
>      g_assert(qemu_mutex_iothread_locked());
> +
> +    /* Block if the fifo is completely empty. */
>      if (fifo8_is_empty(&c->fifo)) {
>          c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs);
>          cs->halted = 1;
> @@ -157,8 +159,14 @@ target_ulong qemu_semihosting_console_inc(CPUState *cs)
>          cpu_loop_exit(cs);
>          /* never returns */
>      }
> -    ch = fifo8_pop(&c->fifo);
> -    return (target_ulong) ch;
> +
> +    /* Read until buffer full or fifo exhausted. */
> +    do {
> +        *(char *)(buf + ret) = fifo8_pop(&c->fifo);
> +        ret++;
> +    } while (ret < len && !fifo8_is_empty(&c->fifo));
> +
> +    return ret;
>  }
>  
>  void qemu_semihosting_console_init(void)
> -- 
> 2.34.1
> 
> 
> 
> 
> To declare a filtering error, please use the following link : https://www.security-mail.net/reporter.php?mid=17c01.629fd5ab.99e5d.0&r=lmichel%40kalrayinc.com&s=qemu-devel-bounces%2Blmichel%3Dkalrayinc.com%40nongnu.org&o=%5BPATCH+v4+43%2F53%5D+semihosting%3A+Expand+qemu_semihosting_console_inc+to+read&verdict=C&c=e9da6e5078994ded7c137213d7c4d1e10afac866
> 

--
diff mbox series

Patch

diff --git a/include/semihosting/console.h b/include/semihosting/console.h
index 27f8e9ae2e..39dbf1b062 100644
--- a/include/semihosting/console.h
+++ b/include/semihosting/console.h
@@ -38,19 +38,21 @@  int qemu_semihosting_console_outs(CPUArchState *env, target_ulong s);
 void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c);
 
 /**
- * qemu_semihosting_console_inc:
+ * qemu_semihosting_console_read:
  * @cs: CPUState
+ * @buf: host buffer
+ * @len: buffer size
  *
- * Receive single character from debug console.  As this call may block
- * if no data is available we suspend the CPU and will re-execute the
+ * Receive at least one character from debug console.  As this call may
+ * block if no data is available we suspend the CPU and will re-execute the
  * instruction when data is there. Therefore two conditions must be met:
  *
  *   - CPUState is synchronized before calling this function
  *   - pc is only updated once the character is successfully returned
  *
- * Returns: character read OR cpu_loop_exit!
+ * Returns: number of characters read, OR cpu_loop_exit!
  */
-target_ulong qemu_semihosting_console_inc(CPUState *cs);
+int qemu_semihosting_console_read(CPUState *cs, void *buf, int len);
 
 /**
  * qemu_semihosting_log_out:
diff --git a/linux-user/semihost.c b/linux-user/semihost.c
index f14c6ae21d..2029fb674c 100644
--- a/linux-user/semihost.c
+++ b/linux-user/semihost.c
@@ -56,21 +56,23 @@  void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr)
  * program is expecting more normal behaviour. This is slow but
  * nothing using semihosting console reading is expecting to be fast.
  */
-target_ulong qemu_semihosting_console_inc(CPUState *cs)
+int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
 {
-    uint8_t c;
+    int ret;
     struct termios old_tio, new_tio;
 
     /* Disable line-buffering and echo */
     tcgetattr(STDIN_FILENO, &old_tio);
     new_tio = old_tio;
     new_tio.c_lflag &= (~ICANON & ~ECHO);
+    new_tio.c_cc[VMIN] = 1;
+    new_tio.c_cc[VTIME] = 0;
     tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
 
-    c = getchar();
+    ret = fread(buf, 1, len, stdin);
 
     /* restore config */
     tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
 
-    return (target_ulong) c;
+    return ret;
 }
diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c
index e7de52d042..20e99cdcc0 100644
--- a/semihosting/arm-compat-semi.c
+++ b/semihosting/arm-compat-semi.c
@@ -427,8 +427,15 @@  void do_common_semihosting(CPUState *cs)
         break;
 
     case TARGET_SYS_READC:
-        ret = qemu_semihosting_console_inc(cs);
-        common_semi_set_ret(cs, ret);
+        {
+            uint8_t ch;
+            int ret = qemu_semihosting_console_read(cs, &ch, 1);
+            if (ret == 1) {
+                common_semi_cb(cs, ch, 0);
+            } else {
+                common_semi_cb(cs, -1, EIO);
+            }
+        }
         break;
 
     case TARGET_SYS_ISERROR:
diff --git a/semihosting/console.c b/semihosting/console.c
index 7b896fe43b..df618a28a4 100644
--- a/semihosting/console.c
+++ b/semihosting/console.c
@@ -144,12 +144,14 @@  static void console_read(void *opaque, const uint8_t *buf, int size)
     c->sleeping_cpus = NULL;
 }
 
-target_ulong qemu_semihosting_console_inc(CPUState *cs)
+int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
 {
-    uint8_t ch;
     SemihostingConsole *c = &console;
+    int ret = 0;
 
     g_assert(qemu_mutex_iothread_locked());
+
+    /* Block if the fifo is completely empty. */
     if (fifo8_is_empty(&c->fifo)) {
         c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs);
         cs->halted = 1;
@@ -157,8 +159,14 @@  target_ulong qemu_semihosting_console_inc(CPUState *cs)
         cpu_loop_exit(cs);
         /* never returns */
     }
-    ch = fifo8_pop(&c->fifo);
-    return (target_ulong) ch;
+
+    /* Read until buffer full or fifo exhausted. */
+    do {
+        *(char *)(buf + ret) = fifo8_pop(&c->fifo);
+        ret++;
+    } while (ret < len && !fifo8_is_empty(&c->fifo));
+
+    return ret;
 }
 
 void qemu_semihosting_console_init(void)