Re: [PATCH v3 00/13] exec: Rework around CPUState user fields (part 2)

2024-04-30 Thread Ilya Leoshkevich
On Tue, Apr 30, 2024 at 09:00:17PM +0200, Philippe Mathieu-Daudé wrote:
> On 30/4/24 20:45, Philippe Mathieu-Daudé wrote:
> > Hi Ilya,
> > 
> > On 30/4/24 19:55, Ilya Leoshkevich wrote:
> > > On Tue, Apr 30, 2024 at 02:27:54PM +0200, Philippe Mathieu-Daudé wrote:
> > > > Missing WASM testing by Ilya (branch available at
> > > > https://gitlab.com/philmd/qemu/-/commits/tcg_flush_jmp_cache)
> > > 
> > > Hmm, it dies very early now:
> > > 
> > >    # gdb --args ./qemu-s390x -L /usr/s390x-linux-gnu 
> > > /build/wasmtime/target/s390x-unknown-linux-gnu/debug/deps/component_fuzz_util-d10a3a6b4ad8af47
> > > 
> > >    Thread 1 "qemu-s390x" received signal SIGSEGV, Segmentation fault.
> > >    0x5559b718 in cpu_common_realizefn (dev=0x557c28c0,
> > > errp=) at
> > > ../home/iii/myrepos/qemu/hw/core/cpu-common.c:217
> > >    217 cpu->accel->plugin_state =
> > > qemu_plugin_create_vcpu_state();
> > > 
> > >    (gdb) bt
> > >    #0  0x5559b718 in cpu_common_realizefn
> > > (dev=0x557c28c0, errp=) at
> > > ../home/iii/myrepos/qemu/hw/core/cpu-common.c:217
> > >    #1  0x5559f59a in s390_cpu_realizefn (dev=0x557c28c0,
> > > errp=0x7fffe1a0) at
> > > ../home/iii/myrepos/qemu/target/s390x/cpu.c:284
> > >    #2  0x5563f76b in device_set_realized (obj= > > out>, value=, errp=0x7fffe2e0) at
> > > ../home/iii/myrepos/qemu/hw/core/qdev.c:510
> > >    #3  0x5564363d in property_set_bool (obj=0x557c28c0,
> > > v=, name=, opaque=0x557a9140,
> > > errp=0x7fffe2e0) at ../home/iii/myrepos/qemu/qom/object.c:2362
> > >    #4  0x55646b9b in object_property_set
> > > (obj=obj@entry=0x557c28c0, name=name@entry=0x556e8ae2
> > > "realized", v=v@entry=0x557c6650,
> > > errp=errp@entry=0x7fffe2e0)
> > >    at ../home/iii/myrepos/qemu/qom/object.c:1471
> > >    #5  0x5564a43f in object_property_set_qobject
> > > (obj=obj@entry=0x557c28c0, name=name@entry=0x556e8ae2
> > > "realized", value=value@entry=0x557a7a90,
> > > errp=errp@entry=0x7fffe2e0)
> > >    at ../home/iii/myrepos/qemu/qom/qom-qobject.c:28
> > >    #6  0x55647204 in object_property_set_bool
> > > (obj=0x557c28c0, name=name@entry=0x556e8ae2 "realized",
> > > value=value@entry=true, errp=errp@entry=0x7fffe2e0)
> > >    at ../home/iii/myrepos/qemu/qom/object.c:1541
> > >    #7  0x5564025c in qdev_realize (dev=,
> > > bus=bus@entry=0x0, errp=errp@entry=0x7fffe2e0) at
> > > ../home/iii/myrepos/qemu/hw/core/qdev.c:291
> > >    #8  0x5559bbb4 in cpu_create (typename=)
> > > at ../home/iii/myrepos/qemu/hw/core/cpu-common.c:61
> > >    #9  0x5559a467 in main (argc=4, argv=0x7fffeaa8,
> > > envp=) at
> > > ../home/iii/myrepos/qemu/linux-user/main.c:811
> > > 
> > >    (gdb) p cpu
> > >    $1 = (CPUState *) 0x557c28c0
> > >    (gdb) p cpu->accel
> > >    $2 = (AccelCPUState *) 0x0
> > > 
> > > Configured with: '/home/iii/myrepos/qemu/configure'
> > > '--target-list=s390x-linux-user' '--disable-tools' '--disable-slirp'
> > > '--disable-fdt' '--disable-capstone' '--disable-docs'
> > > 
> > > If you don't see what can be wrong here right away, I can debug this.
> 
> I added this commit in the same branch:
> 
> -- >8 --
> Author: Philippe Mathieu-Daudé 
> Date:   Tue Apr 30 20:57:15 2024 +0200
> 
> accel/tcg: Initialize TCG plugins in cpu-target.c
> 
> Signed-off-by: Philippe Mathieu-Daudé 
> 
> diff --git a/cpu-target.c b/cpu-target.c
> index 5af120e8aa..585533cfa3 100644
> --- a/cpu-target.c
> +++ b/cpu-target.c
> @@ -46,6 +46,10 @@
>  #include "hw/core/accel-cpu.h"
>  #include "trace/trace-root.h"
>  #include "qemu/accel.h"
> +#ifdef CONFIG_PLUGIN
> +#include "accel/tcg/vcpu-state.h"
> +#include "qemu/plugin.h"
> +#endif
> 
>  #ifndef CONFIG_USER_ONLY
>  static int cpu_common_post_load(void *opaque, int version_id)
> @@ -131,6 +135,13 @@ const VMStateDescription vmstate_cpu_common = {
>  };
>  #endif
> 
> +#ifdef CONFIG_PLUGIN
> +static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data
> unused)
> +{
> +qemu_plugin_vcpu_init_hook(cpu);
> +}
> +#endif
> +
>  bool cpu_exec_rea

Re: [PATCH v3 00/13] exec: Rework around CPUState user fields (part 2)

2024-04-30 Thread Ilya Leoshkevich
On Tue, Apr 30, 2024 at 02:27:54PM +0200, Philippe Mathieu-Daudé wrote:
> Missing WASM testing by Ilya (branch available at
> https://gitlab.com/philmd/qemu/-/commits/tcg_flush_jmp_cache)

Hmm, it dies very early now:

  # gdb --args ./qemu-s390x -L /usr/s390x-linux-gnu 
/build/wasmtime/target/s390x-unknown-linux-gnu/debug/deps/component_fuzz_util-d10a3a6b4ad8af47

  Thread 1 "qemu-s390x" received signal SIGSEGV, Segmentation fault.
  0x5559b718 in cpu_common_realizefn (dev=0x557c28c0, 
errp=) at ../home/iii/myrepos/qemu/hw/core/cpu-common.c:217
  217 cpu->accel->plugin_state = qemu_plugin_create_vcpu_state();

  (gdb) bt
  #0  0x5559b718 in cpu_common_realizefn (dev=0x557c28c0, 
errp=) at ../home/iii/myrepos/qemu/hw/core/cpu-common.c:217
  #1  0x5559f59a in s390_cpu_realizefn (dev=0x557c28c0, 
errp=0x7fffe1a0) at ../home/iii/myrepos/qemu/target/s390x/cpu.c:284
  #2  0x5563f76b in device_set_realized (obj=, 
value=, errp=0x7fffe2e0) at 
../home/iii/myrepos/qemu/hw/core/qdev.c:510
  #3  0x5564363d in property_set_bool (obj=0x557c28c0, v=, name=, opaque=0x557a9140, errp=0x7fffe2e0) at 
../home/iii/myrepos/qemu/qom/object.c:2362
  #4  0x55646b9b in object_property_set (obj=obj@entry=0x557c28c0, 
name=name@entry=0x556e8ae2 "realized", v=v@entry=0x557c6650, 
errp=errp@entry=0x7fffe2e0)
  at ../home/iii/myrepos/qemu/qom/object.c:1471
  #5  0x5564a43f in object_property_set_qobject 
(obj=obj@entry=0x557c28c0, name=name@entry=0x556e8ae2 "realized", 
value=value@entry=0x557a7a90, errp=errp@entry=0x7fffe2e0)
  at ../home/iii/myrepos/qemu/qom/qom-qobject.c:28
  #6  0x55647204 in object_property_set_bool (obj=0x557c28c0, 
name=name@entry=0x556e8ae2 "realized", value=value@entry=true, 
errp=errp@entry=0x7fffe2e0)
  at ../home/iii/myrepos/qemu/qom/object.c:1541
  #7  0x5564025c in qdev_realize (dev=, 
bus=bus@entry=0x0, errp=errp@entry=0x7fffe2e0) at 
../home/iii/myrepos/qemu/hw/core/qdev.c:291
  #8  0x5559bbb4 in cpu_create (typename=) at 
../home/iii/myrepos/qemu/hw/core/cpu-common.c:61
  #9  0x5559a467 in main (argc=4, argv=0x7fffeaa8, envp=) at ../home/iii/myrepos/qemu/linux-user/main.c:811

  (gdb) p cpu
  $1 = (CPUState *) 0x557c28c0
  (gdb) p cpu->accel
  $2 = (AccelCPUState *) 0x0

Configured with: '/home/iii/myrepos/qemu/configure' 
'--target-list=s390x-linux-user' '--disable-tools' '--disable-slirp' 
'--disable-fdt' '--disable-capstone' '--disable-docs'

If you don't see what can be wrong here right away, I can debug this.

> Since v2:
> - Move cpu_loop_exit_requested() to "exec/cpu-loop.h"
> - Added R-b tags
> 
> Since v1:
> - First 13 patches queued
> - Restrict qemu_plugin_vcpu_exit_hook() to (TCG) plugins
> - Restrict cpu_plugin_mem_cbs_enabled() to TCG (plugins)
> - Addressed Richard review comments on the others:
>   - Move cpu_plugin_mem_cbs_enabled()
>   - Do not move mem_io_pc, waiting for [*]
>   - Mention can_do_io restricted
> 
> Finish extracting TCG fields from CPUState:
> - Extract tcg_cpu_exit() from cpu_exit()
> - Introduce AccelOpsClass::exit_vcpu_thread()
> - cpu_exit() calls exit_vcpu_thread=tcg_cpu_exit for TCG
> - Forward declare TaskState and more uses of get_task_state()
> - Introduce TCG AccelCPUState
> - Move TCG specific fields from CPUState to AccelCPUState
> - Restrict "exec/tlb-common.h" to TCG
> - Restrict iommu_notifiers, icount to system emulation
> 
> [*] 
> https://lore.kernel.org/qemu-devel/20240416040609.1313605-3-richard.hender...@linaro.org/
> 
> Based-on: https://gitlab.com/philmd/qemu/-/commits/accel-next
> 
> Philippe Mathieu-Daudé (13):
>   accel/tcg: Restrict qemu_plugin_vcpu_exit_hook() to TCG plugins
>   accel/tcg: Restrict cpu_plugin_mem_cbs_enabled() to TCG
>   accel/tcg: Move @plugin_mem_cbs from CPUState to
> CPUNegativeOffsetState
>   accel/tcg: Move @plugin_state from CPUState to TCG AccelCPUState
>   accel/tcg: Restrict cpu_loop_exit_requested() to TCG
>   accel/tcg: Restrict IcountDecr / can_do_io / CPUTLB to TCG
>   accel/tcg: Move @jmp_env from CPUState to TCG AccelCPUState
>   accel/tcg: Move @cflags_next_tb from CPUState to TCG AccelCPUState
>   accel/tcg: Move @iommu_notifiers from CPUState to TCG AccelCPUState
>   accel/tcg: Move @tcg_cflags from CPUState to TCG AccelCPUState
>   accel/tcg: Restrict icount to system emulation
>   accel/tcg: Move icount fields from CPUState to TCG AccelCPUState
>   accel/tcg: Move @tb_jmp_cache from CPUState to TCG AccelCPUState
> 
>  accel/tcg/internal-common.h  | 18 ++
>  accel/tcg/tb-jmp-cache.h |  4 +--
>  accel/tcg/tcg-accel-ops.h|  1 +
>  accel/tcg/vcpu-state.h   | 20 +++
>  include/exec/cpu-loop.h  | 35 +++
>  include/exec/exec-all.h  | 17 --
>  include/exec/tlb-common.h|  4 +++
>  include/hw/core/cpu.h| 58 

Re: [PATCH v2 10/13] accel/tcg: Remove NULL check in tcg_flush_jmp_cache()

2024-04-29 Thread Ilya Leoshkevich
On Mon, Apr 29, 2024 at 11:30:47PM +0200, Philippe Mathieu-Daudé wrote:
> I /think/ this check added in commit 4e4fa6c12d ("accel/tcg:
> Complete cpu initialization before registration") is now
> unnecessary, but I don't have the WASM reproducer mentioned
> in:
> https://lore.kernel.org/qemu-devel/20221027141856.w5umjgklawgu7pqv@heavy/
> to confirm. Ilya, do you mind testing? If so, we could
> squash this with the previous patch.
> 
> Cc: Ilya Leoshkevich 
> Signed-off-by: Philippe Mathieu-Daudé 
> ---
>  accel/tcg/translate-all.c | 8 +---
>  1 file changed, 1 insertion(+), 7 deletions(-)
> 
> diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
> index ca1e193633..9b02f21b23 100644
> --- a/accel/tcg/translate-all.c
> +++ b/accel/tcg/translate-all.c
> @@ -652,14 +652,8 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
>   */
>  void tcg_flush_jmp_cache(CPUState *cpu)
>  {
> -CPUJumpCache *jc;
> +CPUJumpCache *jc = >accel->tb_jmp_cache;
>  
> -/* During early initialization, the cache may not yet be allocated. */
> -if (unlikely(cpu->accel == NULL)) {
> -return;
> -}
> -
> -jc = >accel->tb_jmp_cache;
>  for (int i = 0; i < TB_JMP_CACHE_SIZE; i++) {
>  qatomic_set(>array[i].tb, NULL);
>  }
> -- 
> 2.41.0
> 

Sure, I'll have a look tomorrow.



[PATCH v2 2/4] linux-user: Fix shmat() strace

2024-03-25 Thread Ilya Leoshkevich
The indices of arguments passed to print_shmat() are all off-by-1,
because arg1 is the ipc() command. Fix them.

New output for linux-shmat-maps test:

3501769 shmat(4784214,0x0080,SHM_RND) = 0

Fixes: 9f7c97324c27 ("linux-user: Add strace for shmat")
Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 linux-user/strace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/linux-user/strace.c b/linux-user/strace.c
index 660f942f599..54169096aa4 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -701,7 +701,7 @@ print_ipc(CPUArchState *cpu_env, const struct syscallname 
*name,
 break;
 case IPCOP_shmat:
 print_shmat(cpu_env, &(const struct syscallname){ .name = "shmat" },
-arg1, arg4, arg2, 0, 0, 0);
+arg2, arg5, arg3, 0, 0, 0);
 break;
 default:
 qemu_log(("%s("
-- 
2.44.0




[PATCH v2 4/4] tests/tcg: Test shmat(NULL)

2024-03-25 Thread Ilya Leoshkevich
Add a small test to prevent regressions.

Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/linux/linux-shmat-null.c | 38 
 1 file changed, 38 insertions(+)
 create mode 100644 tests/tcg/multiarch/linux/linux-shmat-null.c

diff --git a/tests/tcg/multiarch/linux/linux-shmat-null.c 
b/tests/tcg/multiarch/linux/linux-shmat-null.c
new file mode 100644
index 000..94eaaec371a
--- /dev/null
+++ b/tests/tcg/multiarch/linux/linux-shmat-null.c
@@ -0,0 +1,38 @@
+/*
+ * Test shmat(NULL).
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+
+int main(void)
+{
+int shmid;
+char *p;
+int err;
+
+/* Create, attach and intialize shared memory. */
+shmid = shmget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
+assert(shmid != -1);
+p = shmat(shmid, NULL, 0);
+assert(p != (void *)-1);
+*p = 42;
+
+/* Reattach, check that the value is still there. */
+err = shmdt(p);
+assert(err == 0);
+p = shmat(shmid, NULL, 0);
+assert(p != (void *)-1);
+assert(*p == 42);
+
+/* Detach. */
+err = shmdt(p);
+assert(err == 0);
+err = shmctl(shmid, IPC_RMID, NULL);
+assert(err == 0);
+
+return EXIT_SUCCESS;
+}
-- 
2.44.0




[PATCH v2 0/4] linux-user: Fix shmat(NULL) for h != g

2024-03-25 Thread Ilya Leoshkevich
v1: 
https://lore.kernel.org/qemu-devel/20240325153313.526888-1-...@linux.ibm.com/
v1 -> v2: Remove an unnecessary ifdef, add R-Bs (Richard).

Hi,

I noticed that while shmat() now works with /proc/self/maps,
shmat(NULL) got broken. This series fixes that along with two related
strace issues, and adds a test.

Best regards,
Ilya


Ilya Leoshkevich (4):
  linux-user: Fix semctl() strace
  linux-user: Fix shmat() strace
  linux-user: Fix shmat(NULL) for h != g
  tests/tcg: Test shmat(NULL)

 linux-user/mmap.c|  2 +-
 linux-user/strace.c  | 10 ++
 tests/tcg/multiarch/linux/linux-shmat-null.c | 38 
 3 files changed, 42 insertions(+), 8 deletions(-)
 create mode 100644 tests/tcg/multiarch/linux/linux-shmat-null.c

-- 
2.44.0




[PATCH v2 1/4] linux-user: Fix semctl() strace

2024-03-25 Thread Ilya Leoshkevich
The indices of arguments used with semctl() are all off-by-1, because
arg1 is the ipc() command. Fix them. While at it, reuse print_semctl().

New output (for a small test program):

3540333 semctl(999,888,SEM_INFO,0x7fe5051ee9a0) = -1 errno=14 (Bad 
address)

Fixes: 7ccfb2eb5f9d ("Fix warnings that would be caused by gcc flag 
-Wwrite-strings")
Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 linux-user/strace.c | 8 ++--
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/linux-user/strace.c b/linux-user/strace.c
index 9934e2208e2..660f942f599 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -657,7 +657,6 @@ print_newselect(CPUArchState *cpu_env, const struct 
syscallname *name,
 }
 #endif
 
-#ifdef TARGET_NR_semctl
 static void
 print_semctl(CPUArchState *cpu_env, const struct syscallname *name,
  abi_long arg1, abi_long arg2, abi_long arg3,
@@ -668,7 +667,6 @@ print_semctl(CPUArchState *cpu_env, const struct 
syscallname *name,
 print_ipc_cmd(arg3);
 qemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4);
 }
-#endif
 
 static void
 print_shmat(CPUArchState *cpu_env, const struct syscallname *name,
@@ -698,10 +696,8 @@ print_ipc(CPUArchState *cpu_env, const struct syscallname 
*name,
 {
 switch(arg1) {
 case IPCOP_semctl:
-qemu_log("semctl(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",",
- arg1, arg2);
-print_ipc_cmd(arg3);
-qemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4);
+print_semctl(cpu_env, &(const struct syscallname){ .name = "semctl" },
+ arg2, arg3, arg4, arg5, 0, 0);
 break;
 case IPCOP_shmat:
 print_shmat(cpu_env, &(const struct syscallname){ .name = "shmat" },
-- 
2.44.0




[PATCH v2 3/4] linux-user: Fix shmat(NULL) for h != g

2024-03-25 Thread Ilya Leoshkevich
In the h != g && shmaddr == NULL && !reserved_va case, target_shmat()
incorrectly mmap()s the initial anonymous range with
MAP_FIXED_NOREPLACE, even though the earlier mmap_find_vma() has
already reserved the respective address range.

Fix by using MAP_FIXED when "mapped", which is set after
mmap_find_vma(), is true.

Fixes: 78bc8ed9a8f0 ("linux-user: Rewrite target_shmat")
Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 linux-user/mmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index e88faf1ab3d..681b6db1b67 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -1358,7 +1358,7 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
 if (h_len != t_len) {
 int mmap_p = PROT_READ | (shmflg & SHM_RDONLY ? 0 : PROT_WRITE);
 int mmap_f = MAP_PRIVATE | MAP_ANONYMOUS
-   | (reserved_va || (shmflg & SHM_REMAP)
+   | (reserved_va || mapped || (shmflg & SHM_REMAP)
   ? MAP_FIXED : MAP_FIXED_NOREPLACE);
 
 test = mmap(want, m_len, mmap_p, mmap_f, -1, 0);
-- 
2.44.0




[PATCH 0/4] linux-user: Fix shmat(NULL) for h != g

2024-03-25 Thread Ilya Leoshkevich
Hi,

I noticed that while shmat() now works with /proc/self/maps,
shmat(NULL) got broken. This series fixes that along with two related
strace issues, and adds a test.

Best regards,
Ilya

Ilya Leoshkevich (4):
  linux-user: Fix semctl() strace
  linux-user: Fix shmat() strace
  linux-user: Fix shmat(NULL) for h != g
  tests/tcg: Check that shmat(NULL)

 linux-user/mmap.c|  2 +-
 linux-user/strace.c  | 10 +++---
 tests/tcg/multiarch/linux/linux-shmat-null.c | 38 
 3 files changed, 43 insertions(+), 7 deletions(-)
 create mode 100644 tests/tcg/multiarch/linux/linux-shmat-null.c

-- 
2.44.0




[PATCH 2/4] linux-user: Fix shmat() strace

2024-03-25 Thread Ilya Leoshkevich
The indices of arguments passed to print_shmat() are all off-by-1,
because arg1 is the ipc() command. Fix them.

New output for linux-shmat-maps test:

3501769 shmat(4784214,0x0080,SHM_RND) = 0

Fixes: 9f7c97324c27 ("linux-user: Add strace for shmat")
Signed-off-by: Ilya Leoshkevich 
---
 linux-user/strace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/linux-user/strace.c b/linux-user/strace.c
index 9be71af4016..3b4ccd9fa04 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -703,7 +703,7 @@ print_ipc(CPUArchState *cpu_env, const struct syscallname 
*name,
 break;
 case IPCOP_shmat:
 print_shmat(cpu_env, &(const struct syscallname){ .name = "shmat" },
-arg1, arg4, arg2, 0, 0, 0);
+arg2, arg5, arg3, 0, 0, 0);
 break;
 default:
 qemu_log(("%s("
-- 
2.44.0




[PATCH 1/4] linux-user: Fix semctl() strace

2024-03-25 Thread Ilya Leoshkevich
The indices of arguments used with semctl() are all off-by-1, because
arg1 is the ipc() command. Fix them. While at it, reuse print_semctl().

New output (for a small test program):

3540333 semctl(999,888,SEM_INFO,0x7fe5051ee9a0) = -1 errno=14 (Bad 
address)

Fixes: 7ccfb2eb5f9d ("Fix warnings that would be caused by gcc flag 
-Wwrite-strings")
Signed-off-by: Ilya Leoshkevich 
---
 linux-user/strace.c | 8 +++-
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/linux-user/strace.c b/linux-user/strace.c
index 9934e2208e2..9be71af4016 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -657,7 +657,7 @@ print_newselect(CPUArchState *cpu_env, const struct 
syscallname *name,
 }
 #endif
 
-#ifdef TARGET_NR_semctl
+#if defined(TARGET_NR_semctl) || defined(TARGET_NR_ipc)
 static void
 print_semctl(CPUArchState *cpu_env, const struct syscallname *name,
  abi_long arg1, abi_long arg2, abi_long arg3,
@@ -698,10 +698,8 @@ print_ipc(CPUArchState *cpu_env, const struct syscallname 
*name,
 {
 switch(arg1) {
 case IPCOP_semctl:
-qemu_log("semctl(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",",
- arg1, arg2);
-print_ipc_cmd(arg3);
-qemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4);
+print_semctl(cpu_env, &(const struct syscallname){ .name = "semctl" },
+ arg2, arg3, arg4, arg5, 0, 0);
 break;
 case IPCOP_shmat:
 print_shmat(cpu_env, &(const struct syscallname){ .name = "shmat" },
-- 
2.44.0




[PATCH 4/4] tests/tcg: Test shmat(NULL)

2024-03-25 Thread Ilya Leoshkevich
Add a small test to prevent regressions.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/linux/linux-shmat-null.c | 38 
 1 file changed, 38 insertions(+)
 create mode 100644 tests/tcg/multiarch/linux/linux-shmat-null.c

diff --git a/tests/tcg/multiarch/linux/linux-shmat-null.c 
b/tests/tcg/multiarch/linux/linux-shmat-null.c
new file mode 100644
index 000..94eaaec371a
--- /dev/null
+++ b/tests/tcg/multiarch/linux/linux-shmat-null.c
@@ -0,0 +1,38 @@
+/*
+ * Test shmat(NULL).
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+
+int main(void)
+{
+int shmid;
+char *p;
+int err;
+
+/* Create, attach and intialize shared memory. */
+shmid = shmget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
+assert(shmid != -1);
+p = shmat(shmid, NULL, 0);
+assert(p != (void *)-1);
+*p = 42;
+
+/* Reattach, check that the value is still there. */
+err = shmdt(p);
+assert(err == 0);
+p = shmat(shmid, NULL, 0);
+assert(p != (void *)-1);
+assert(*p == 42);
+
+/* Detach. */
+err = shmdt(p);
+assert(err == 0);
+err = shmctl(shmid, IPC_RMID, NULL);
+assert(err == 0);
+
+return EXIT_SUCCESS;
+}
-- 
2.44.0




[PATCH 3/4] linux-user: Fix shmat(NULL) for h != g

2024-03-25 Thread Ilya Leoshkevich
In the h != g && shmaddr == NULL && !reserved_va case, target_shmat()
incorrectly mmap()s the initial anonymous range with
MAP_FIXED_NOREPLACE, even though the earlier mmap_find_vma() has
already reserved the respective address range.

Fix by using MAP_FIXED when "mapped", which is set after
mmap_find_vma(), is true.

Fixes: 78bc8ed9a8f0 ("linux-user: Rewrite target_shmat")
Signed-off-by: Ilya Leoshkevich 
---
 linux-user/mmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index e88faf1ab3d..681b6db1b67 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -1358,7 +1358,7 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
 if (h_len != t_len) {
 int mmap_p = PROT_READ | (shmflg & SHM_RDONLY ? 0 : PROT_WRITE);
 int mmap_f = MAP_PRIVATE | MAP_ANONYMOUS
-   | (reserved_va || (shmflg & SHM_REMAP)
+   | (reserved_va || mapped || (shmflg & SHM_REMAP)
   ? MAP_FIXED : MAP_FIXED_NOREPLACE);
 
 test = mmap(want, m_len, mmap_p, mmap_f, -1, 0);
-- 
2.44.0




[PATCH v2 1/2] target/s390x: Use mutable temporary value for op_ts

2024-03-18 Thread Ilya Leoshkevich
From: Ido Plat 

Otherwise TCG would assume the register that holds t1 would be constant
and reuse whenever it needs the value within it.

Cc: qemu-sta...@nongnu.org
Fixes: f1ea739bd598 ("target/s390x: Use tcg_constant_* in local contexts")
Reviewed-by: Ilya Leoshkevich 
Reviewed-by: Richard Henderson 
[iii: Adjust a newline and capitalization, add tags]
Signed-off-by: Ido Plat 
Signed-off-by: Ilya Leoshkevich 
---
 target/s390x/tcg/translate.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index 0d0c672c959..57b7db1ee96 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -4781,9 +4781,10 @@ static DisasJumpType op_trXX(DisasContext *s, DisasOps 
*o)
 
 static DisasJumpType op_ts(DisasContext *s, DisasOps *o)
 {
-TCGv_i32 t1 = tcg_constant_i32(0xff);
+TCGv_i32 ff = tcg_constant_i32(0xff);
+TCGv_i32 t1 = tcg_temp_new_i32();
 
-tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB);
+tcg_gen_atomic_xchg_i32(t1, o->in2, ff, get_mem_index(s), MO_UB);
 tcg_gen_extract_i32(cc_op, t1, 7, 1);
 set_cc_static(s);
 return DISAS_NEXT;
-- 
2.44.0




[PATCH v2 2/2] tests/tcg/s390x: Test TEST AND SET

2024-03-18 Thread Ilya Leoshkevich
Add a small test to prevent regressions.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/s390x/Makefile.target |  1 +
 tests/tcg/s390x/ts.c| 35 +
 2 files changed, 36 insertions(+)
 create mode 100644 tests/tcg/s390x/ts.c

diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index e2aba2ec274..a8f86c94498 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -47,6 +47,7 @@ TESTS+=add-logical-with-carry
 TESTS+=lae
 TESTS+=cvd
 TESTS+=cvb
+TESTS+=ts
 
 cdsg: CFLAGS+=-pthread
 cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/ts.c b/tests/tcg/s390x/ts.c
new file mode 100644
index 000..441faf30d98
--- /dev/null
+++ b/tests/tcg/s390x/ts.c
@@ -0,0 +1,35 @@
+/*
+ * Test the TEST AND SET instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+
+static int ts(char *p)
+{
+int cc;
+
+asm("ts %[p]\n"
+"ipm %[cc]"
+: [cc] "=r" (cc)
+, [p] "+Q" (*p)
+: : "cc");
+
+return (cc >> 28) & 3;
+}
+
+int main(void)
+{
+char c;
+
+c = 0x80;
+assert(ts() == 1);
+assert(c == 0xff);
+
+c = 0x7f;
+assert(ts() == 0);
+assert(c == 0xff);
+
+return EXIT_SUCCESS;
+}
-- 
2.44.0




[PATCH 2/2] tests/tcg/s390x: Test TEST AND SET

2024-03-18 Thread Ilya Leoshkevich
Add a small test to prevent regressions.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/s390x/Makefile.target |  1 +
 tests/tcg/s390x/ts.c| 35 +
 2 files changed, 36 insertions(+)
 create mode 100644 tests/tcg/s390x/ts.c

diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index e2aba2ec274..a8f86c94498 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -47,6 +47,7 @@ TESTS+=add-logical-with-carry
 TESTS+=lae
 TESTS+=cvd
 TESTS+=cvb
+TESTS+=ts
 
 cdsg: CFLAGS+=-pthread
 cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/ts.c b/tests/tcg/s390x/ts.c
new file mode 100644
index 000..441faf30d98
--- /dev/null
+++ b/tests/tcg/s390x/ts.c
@@ -0,0 +1,35 @@
+/*
+ * Test the TEST AND SET instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+
+static int ts(char *p)
+{
+int cc;
+
+asm("ts %[p]\n"
+"ipm %[cc]"
+: [cc] "=r" (cc)
+, [p] "+Q" (*p)
+: : "cc");
+
+return (cc >> 28) & 3;
+}
+
+int main(void)
+{
+char c;
+
+c = 0x80;
+assert(ts() == 1);
+assert(c == 0xff);
+
+c = 0x7f;
+assert(ts() == 0);
+assert(c == 0xff);
+
+return EXIT_SUCCESS;
+}
-- 
2.44.0




[PATCH 1/2] target/s390x: Use mutable temporary value for op_ts

2024-03-18 Thread Ilya Leoshkevich
From: Ido Plat 

Otherwise TCG would assume the register that holds t1 would be constant
and reuse whenever it needs the value within it.

Reviewed-by: Ilya Leoshkevich 
[iii: Adjust a newline and capitalization]
Signed-off-by: Ido Plat 
---
 target/s390x/tcg/translate.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index 0d0c672c959..3fdddac7684 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -4781,8 +4781,9 @@ static DisasJumpType op_trXX(DisasContext *s, DisasOps *o)
 
 static DisasJumpType op_ts(DisasContext *s, DisasOps *o)
 {
-TCGv_i32 t1 = tcg_constant_i32(0xff);
+TCGv_i32 t1 = tcg_temp_new_i32();
 
+tcg_gen_movi_i32(t1, 0xff);
 tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB);
 tcg_gen_extract_i32(cc_op, t1, 7, 1);
 set_cc_static(s);
-- 
2.44.0




[PATCH] meson: Make DEBUG_REMAP a meson option

2024-03-11 Thread Ilya Leoshkevich
Currently DEBUG_REMAP is a macro that needs to be manually #defined to
be activated, which makes it hard to have separate build directories
dedicated to testing the code with it. Promote it to a meson option.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/qemu.h   | 6 ++
 linux-user/qemu.h | 4 +---
 linux-user/uaccess.c  | 4 ++--
 meson.build   | 4 
 meson_options.txt | 2 ++
 scripts/meson-buildoptions.sh | 3 +++
 6 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 1b0a591d2d2..8629f0dcde9 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -22,8 +22,6 @@
 #include "exec/cpu_ldst.h"
 #include "exec/exec-all.h"
 
-#undef DEBUG_REMAP
-
 #include "exec/user/abitypes.h"
 
 extern char **environ;
@@ -437,7 +435,7 @@ static inline void *lock_user(int type, abi_ulong 
guest_addr, long len,
 if (!access_ok(type, guest_addr, len)) {
 return NULL;
 }
-#ifdef DEBUG_REMAP
+#ifdef CONFIG_DEBUG_REMAP
 {
 void *addr;
 addr = g_malloc(len);
@@ -461,7 +459,7 @@ static inline void unlock_user(void *host_ptr, abi_ulong 
guest_addr,
long len)
 {
 
-#ifdef DEBUG_REMAP
+#ifdef CONFIG_DEBUG_REMAP
 if (!host_ptr) {
 return;
 }
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 32cd43d9eff..4777856b529 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -4,8 +4,6 @@
 #include "cpu.h"
 #include "exec/cpu_ldst.h"
 
-#undef DEBUG_REMAP
-
 #include "exec/user/abitypes.h"
 
 #include "syscall_defs.h"
@@ -332,7 +330,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t 
len, bool copy);
 /* Unlock an area of guest memory.  The first LEN bytes must be
flushed back to guest memory. host_ptr = NULL is explicitly
allowed and does nothing. */
-#ifndef DEBUG_REMAP
+#ifndef CONFIG_DEBUG_REMAP
 static inline void unlock_user(void *host_ptr, abi_ulong guest_addr,
ssize_t len)
 {
diff --git a/linux-user/uaccess.c b/linux-user/uaccess.c
index 425cbf677f7..27e841e6510 100644
--- a/linux-user/uaccess.c
+++ b/linux-user/uaccess.c
@@ -14,7 +14,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, 
bool copy)
 return NULL;
 }
 host_addr = g2h_untagged(guest_addr);
-#ifdef DEBUG_REMAP
+#ifdef CONFIG_DEBUG_REMAP
 if (copy) {
 host_addr = g_memdup(host_addr, len);
 } else {
@@ -24,7 +24,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, 
bool copy)
 return host_addr;
 }
 
-#ifdef DEBUG_REMAP
+#ifdef CONFIG_DEBUG_REMAP
 void unlock_user(void *host_ptr, abi_ulong guest_addr, ssize_t len)
 {
 void *host_ptr_conv;
diff --git a/meson.build b/meson.build
index f9dbe7634e5..1427e9f8811 100644
--- a/meson.build
+++ b/meson.build
@@ -2342,6 +2342,7 @@ config_host_data.set('CONFIG_DEBUG_GRAPH_LOCK', 
get_option('debug_graph_lock'))
 config_host_data.set('CONFIG_DEBUG_MUTEX', get_option('debug_mutex'))
 config_host_data.set('CONFIG_DEBUG_STACK_USAGE', 
get_option('debug_stack_usage'))
 config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg'))
+config_host_data.set('CONFIG_DEBUG_REMAP', get_option('debug_remap'))
 config_host_data.set('CONFIG_LIVE_BLOCK_MIGRATION', 
get_option('live_block_migration').allowed())
 config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug'))
 config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed())
@@ -4285,6 +4286,9 @@ if config_all_accel.has_key('CONFIG_TCG')
   endif
   summary_info += {'TCG plugins':   get_option('plugins')}
   summary_info += {'TCG debug enabled': get_option('debug_tcg')}
+  if have_linux_user or have_bsd_user
+summary_info += {'syscall buffer debugging support': 
get_option('debug_remap')}
+  endif
 endif
 summary_info += {'target list':   ' '.join(target_dirs)}
 if have_system
diff --git a/meson_options.txt b/meson_options.txt
index 0a99a059ec8..7cd48a88e6e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -85,6 +85,8 @@ option('plugins', type: 'boolean', value: false,
description: 'TCG plugins via shared library loading')
 option('debug_tcg', type: 'boolean', value: false,
description: 'TCG debugging')
+option('debug_remap', type: 'boolean', value: false,
+   description: 'syscall buffer debugging support')
 option('tcg_interpreter', type: 'boolean', value: false,
description: 'TCG with bytecode interpreter (slow)')
 option('safe_stack', type: 'boolean', value: false,
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 680fa3f581d..aff58275daf 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -29,6 +29,7 @@ meson_options_help() {
   printf "%s\n" '  --enable-debug-graph-lock'
   printf "%s\n" '   graph lock debugging su

[PATCH] gdbstub: Fix double close() of the follow-fork-mode socket

2024-03-11 Thread Ilya Leoshkevich
When the terminal GDB_FORK_ENABLED state is reached, the coordination
socket is not needed anymore and is therefore closed. However, if there
is a communication error between QEMU gdbstub and GDB, the generic
error handling code attempts to close it again.

Fix by closing it later - before returning - instead.

Fixes: Coverity CID 1539966
Fixes: d547e711a8a5 ("gdbstub: Implement follow-fork-mode child")
Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 7f9f19a1249..08aed022e26 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -502,6 +502,7 @@ void gdbserver_fork_end(CPUState *cpu, pid_t pid)
 switch (gdbserver_user_state.fork_state) {
 case GDB_FORK_ENABLED:
 if (gdbserver_user_state.running_state) {
+close(fd);
 return;
 }
 QEMU_FALLTHROUGH;
@@ -527,7 +528,6 @@ void gdbserver_fork_end(CPUState *cpu, pid_t pid)
 gdbserver_user_state.fork_state = GDB_FORK_ACTIVE;
 break;
 case GDB_FORK_ENABLE:
-close(fd);
 gdbserver_user_state.fork_state = GDB_FORK_ENABLED;
 break;
 case GDB_FORK_DISABLE:
@@ -542,7 +542,6 @@ void gdbserver_fork_end(CPUState *cpu, pid_t pid)
 if (write(fd, , 1) != 1) {
 goto fail;
 }
-close(fd);
 gdbserver_user_state.fork_state = GDB_FORK_ENABLED;
 break;
 case GDB_FORK_DISABLING:
-- 
2.44.0




Re: [PATCH] linux-user: Make TARGET_NR_setgroups affect only the current thread

2024-02-26 Thread Ilya Leoshkevich
On Wed, Jan 31, 2024 at 02:10:55PM +0100, Philippe Mathieu-Daudé wrote:
> On 31/1/24 01:18, Ilya Leoshkevich wrote:
> > Like TARGET_NR_setuid, TARGET_NR_setgroups should affect only the
> > calling thread, and not the entire process. Therefore, implement it
> > using a syscall, and not a libc call.
> > 
> > Cc: qemu-sta...@nongnu.org
> > Fixes: 19b84f3c35d7 ("added setgroups and getgroups syscalls")
> > Signed-off-by: Ilya Leoshkevich 
> > ---
> >   linux-user/syscall.c | 10 --
> >   1 file changed, 8 insertions(+), 2 deletions(-)
> 
> Reviewed-by: Philippe Mathieu-Daudé 

Thanks for the review!

A few weeks have passed and I wonder if this patch fell through the
cracks. Do I need to do something to have it applied?



Re: [PATCH 3/3] linux-user: Rewrite target_shmat

2024-02-23 Thread Ilya Leoshkevich
On Thu, Feb 22, 2024 at 05:03:09PM -1000, Richard Henderson wrote:
> Handle combined host and guest alignment requirements.
> Handle host and guest page size differences.
> Handle SHM_EXEC.
> 
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/115
> Signed-off-by: Richard Henderson 
> ---
>  linux-user/mmap.c | 146 ++
>  1 file changed, 110 insertions(+), 36 deletions(-)

[...]

> -/* find out the length of the shared memory segment */
> +/*
> + * Because we can't use host shmat() unless the address is sufficiently
> + * aligned for the host, we'll need to check both.
> + * TODO: Could be fixed with softmmu.
> + */

Are there any plans to introduce softmmu to qemu-user?

[...]

Reviewed-by: Ilya Leoshkevich 



Please consider adding the reproducer to the series:

>From 964164ada4de55ac01a56613f7b759e538803fc9 Mon Sep 17 00:00:00 2001
From: Ilya Leoshkevich 
Date: Fri, 23 Feb 2024 12:31:40 +0100
Subject: [PATCH] tests/tcg: Check that shmat() does not break /proc/self/maps

Add a regression test for a recently fixed issue, where shmat()
desynced the guest and the host view of the address space and caused
open("/proc/self/maps") to SEGV.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/linux/linux-shmat-maps.c | 40 
 1 file changed, 40 insertions(+)
 create mode 100644 tests/tcg/multiarch/linux/linux-shmat-maps.c

diff --git a/tests/tcg/multiarch/linux/linux-shmat-maps.c 
b/tests/tcg/multiarch/linux/linux-shmat-maps.c
new file mode 100644
index 000..4090bc77ba7
--- /dev/null
+++ b/tests/tcg/multiarch/linux/linux-shmat-maps.c
@@ -0,0 +1,40 @@
+/*
+ * Test that shmat() does not break /proc/self/maps.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+int main(void)
+{
+char buf[128];
+int err, fd;
+int shmid;
+ssize_t n;
+void *p;
+
+shmid = shmget(IPC_PRIVATE, 0x400, IPC_CREAT | 0600);
+assert(shmid != -1);
+p = shmat(shmid, (void *)0x80, 0);
+assert(p != (void *)-1);
+
+fd = open("/proc/self/maps", O_RDONLY);
+assert(fd != -1);
+do {
+n = read(fd, buf, sizeof(buf));
+assert(n >= 0);
+} while (n != 0);
+close(fd);
+
+err = shmdt(p);
+assert(err == 0);
+err = shmctl(shmid, IPC_RMID, NULL);
+assert(err == 0);
+
+return EXIT_SUCCESS;
+}
-- 
2.34.1




[PATCH v4 03/12] {linux,bsd}-user: Update ts_tid after fork()

2024-02-19 Thread Ilya Leoshkevich
Currently ts_tid contains the parent tid after fork(), which is not
correct. So far it has not affected anything, but the upcoming
follow-fork-mode child support relies on the correct value, so fix it.

Reviewed-by: Alex Bennée 
Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c   | 1 +
 linux-user/main.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index e5efb7b8458..72289673a94 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -127,6 +127,7 @@ void fork_end(int child)
  * state, so we don't need to end_exclusive() here.
  */
 qemu_init_cpu_list();
+get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
 gdbserver_fork(thread_cpu);
 } else {
 mmap_fork_end(child);
diff --git a/linux-user/main.c b/linux-user/main.c
index 74b2fbb3938..1d53f708354 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -160,6 +160,7 @@ void fork_end(int child)
 }
 }
 qemu_init_cpu_list();
+get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
 gdbserver_fork(thread_cpu);
 } else {
 cpu_list_unlock();
-- 
2.43.2




[PATCH v4 02/12] {linux,bsd}-user: Introduce get_task_state()

2024-02-19 Thread Ilya Leoshkevich
A CPU's TaskState is stored in the CPUState's void *opaque field,
accessing which is somewhat awkward due to having to use a cast.
Introduce a wrapper and use it everywhere.

Suggested-by: Alex Bennée 
Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/bsd-file.h   |  2 +-
 bsd-user/qemu.h   |  5 +
 bsd-user/signal.c | 20 ++--
 gdbstub/user-target.c |  4 ++--
 include/user/safe-syscall.h   |  2 +-
 linux-user/aarch64/cpu_loop.c |  2 +-
 linux-user/arm/cpu_loop.c |  4 ++--
 linux-user/arm/signal.c   |  2 +-
 linux-user/cris/cpu_loop.c|  2 +-
 linux-user/elfload.c  |  6 +++---
 linux-user/hppa/signal.c  |  2 +-
 linux-user/linuxload.c|  2 +-
 linux-user/m68k/cpu_loop.c|  2 +-
 linux-user/m68k/target_cpu.h  |  2 +-
 linux-user/mips/cpu_loop.c|  2 +-
 linux-user/ppc/signal.c   |  4 ++--
 linux-user/qemu.h |  5 +
 linux-user/riscv/cpu_loop.c   |  2 +-
 linux-user/signal-common.h|  2 +-
 linux-user/signal.c   | 30 +++---
 linux-user/syscall.c  | 26 +-
 linux-user/vm86.c | 18 +-
 linux-user/xtensa/signal.c|  2 +-
 plugins/api.c |  8 
 semihosting/arm-compat-semi.c |  8 
 25 files changed, 87 insertions(+), 77 deletions(-)

diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h
index 3c00dc00567..6fa2c30b4de 100644
--- a/bsd-user/bsd-file.h
+++ b/bsd-user/bsd-file.h
@@ -641,7 +641,7 @@ static abi_long do_bsd_readlink(CPUArchState *env, abi_long 
arg1,
 }
 if (strcmp(p1, "/proc/curproc/file") == 0) {
 CPUState *cpu = env_cpu(env);
-TaskState *ts = (TaskState *)cpu->opaque;
+TaskState *ts = get_task_state(cpu);
 strncpy(p2, ts->bprm->fullpath, arg3);
 ret = MIN((abi_long)strlen(ts->bprm->fullpath), arg3);
 } else {
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index dc842fffa7d..a2417b25156 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -110,6 +110,11 @@ typedef struct TaskState {
 struct target_sigaltstack sigaltstack_used;
 } __attribute__((aligned(16))) TaskState;
 
+static inline TaskState *get_task_state(CPUState *cs)
+{
+return cs->opaque;
+}
+
 void stop_all_tasks(void);
 extern const char *interp_prefix;
 extern const char *qemu_uname_release;
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index f4352e4530f..e9f80a06d32 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -319,7 +319,7 @@ void host_to_target_siginfo(target_siginfo_t *tinfo, const 
siginfo_t *info)
 
 int block_signals(void)
 {
-TaskState *ts = (TaskState *)thread_cpu->opaque;
+TaskState *ts = get_task_state(thread_cpu);
 sigset_t set;
 
 /*
@@ -359,7 +359,7 @@ void dump_core_and_abort(int target_sig)
 {
 CPUState *cpu = thread_cpu;
 CPUArchState *env = cpu_env(cpu);
-TaskState *ts = cpu->opaque;
+TaskState *ts = get_task_state(cpu);
 int core_dumped = 0;
 int host_sig;
 struct sigaction act;
@@ -421,7 +421,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type,
   target_siginfo_t *info)
 {
 CPUState *cpu = env_cpu(env);
-TaskState *ts = cpu->opaque;
+TaskState *ts = get_task_state(cpu);
 
 trace_user_queue_signal(env, sig);
 
@@ -476,7 +476,7 @@ void force_sig_fault(int sig, int code, abi_ulong addr)
 static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
 {
 CPUState *cpu = thread_cpu;
-TaskState *ts = cpu->opaque;
+TaskState *ts = get_task_state(cpu);
 target_siginfo_t tinfo;
 ucontext_t *uc = puc;
 struct emulated_sigtable *k;
@@ -585,7 +585,7 @@ static void host_signal_handler(int host_sig, siginfo_t 
*info, void *puc)
 /* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */
 abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
 {
-TaskState *ts = (TaskState *)thread_cpu->opaque;
+TaskState *ts = get_task_state(thread_cpu);
 int ret;
 target_stack_t oss;
 
@@ -714,7 +714,7 @@ int do_sigaction(int sig, const struct target_sigaction 
*act,
 static inline abi_ulong get_sigframe(struct target_sigaction *ka,
 CPUArchState *env, size_t frame_size)
 {
-TaskState *ts = (TaskState *)thread_cpu->opaque;
+TaskState *ts = get_task_state(thread_cpu);
 abi_ulong sp;
 
 /* Use default user stack */
@@ -789,7 +789,7 @@ static int reset_signal_mask(target_ucontext_t *ucontext)
 int i;
 sigset_t blocked;
 target_sigset_t target_set;
-TaskState *ts = (TaskState *)thread_cpu->opaque;
+TaskState *ts = get_task_state(thread_cpu);
 
 for (i = 0; i < TARGET_NSIG_WORDS; i++) {
 __get_user(target_set.__bits[i], >uc_sigmask.__bits[i]);
@@ -839,7 +839,7 @@ badframe:
 
 void signal_init(void)
 {
-TaskState *ts = (TaskState *)thread_cpu-

[PATCH v4 00/12] gdbstub: Implement follow-fork-mode child

2024-02-19 Thread Ilya Leoshkevich
v3: https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg03142.html
v3 -> v4: Address the review comments, add R-bs.
  Add the get_task_state() refactoring.
  Keep passing CPUState to gdbserver_fork_end() for tb_flush().
  Patches that need review: 02/12, 09/12-12/12.

v2: https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg00810.html
v2 -> v3: Rebase on top of master.
  Fix a typo in the 01/11 commit message.

v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06646.html
v1 -> v2: Factor out a number of prep patches;
  Add a state transition diagram comment (Alex).
  Improve a few comments;
  Extend the ts_tid fix to bsd.

Hi,

I needed to debug a linux-user crash between fork() and exec() [1] and
realized that gdbstub does not allow this. This series lifts this
restriction (one still cannot debug past exec() though). Patches 1-10
are preliminary refactorings, patch 11 is the implementation, and patch
12 is the test.

[1] https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06424.html

Best regards,
Ilya

Ilya Leoshkevich (12):
  gdbstub: Support disablement in a multi-threaded process
  {linux,bsd}-user: Introduce get_task_state()
  {linux,bsd}-user: Update ts_tid after fork()
  gdbstub: Introduce gdbserver_fork_start()
  {linux,bsd}-user: Pass pid to fork_end()
  {linux,bsd}-user: Pass pid to gdbserver_fork()
  gdbstub: Call gdbserver_fork() both in parent and in child
  gdbstub: Introduce gdb_handle_query_supported_user()
  gdbstub: Introduce gdb_handle_set_thread_user()
  gdbstub: Introduce gdb_handle_detach_user()
  gdbstub: Implement follow-fork-mode child
  tests/tcg: Add two follow-fork-mode tests

 bsd-user/bsd-file.h   |   2 +-
 bsd-user/freebsd/os-proc.h|   6 +-
 bsd-user/main.c   |   9 +-
 bsd-user/qemu.h   |   7 +-
 bsd-user/signal.c |  20 +-
 gdbstub/gdbstub.c |  29 ++-
 gdbstub/internals.h   |   3 +
 gdbstub/user-target.c |   4 +-
 gdbstub/user.c| 244 +-
 include/gdbstub/user.h|  10 +-
 include/user/safe-syscall.h   |   2 +-
 linux-user/aarch64/cpu_loop.c |   2 +-
 linux-user/arm/cpu_loop.c |   4 +-
 linux-user/arm/signal.c   |   2 +-
 linux-user/cris/cpu_loop.c|   2 +-
 linux-user/elfload.c  |   6 +-
 linux-user/hppa/signal.c  |   2 +-
 linux-user/linuxload.c|   2 +-
 linux-user/m68k/cpu_loop.c|   2 +-
 linux-user/m68k/target_cpu.h  |   2 +-
 linux-user/main.c |   8 +-
 linux-user/mips/cpu_loop.c|   2 +-
 linux-user/ppc/signal.c   |   4 +-
 linux-user/qemu.h |   5 +
 linux-user/riscv/cpu_loop.c   |   2 +-
 linux-user/signal-common.h|   2 +-
 linux-user/signal.c   |  30 +--
 linux-user/syscall.c  |  32 +--
 linux-user/user-internals.h   |   2 +-
 linux-user/vm86.c |  18 +-
 linux-user/xtensa/signal.c|   2 +-
 plugins/api.c |   8 +-
 semihosting/arm-compat-semi.c |   8 +-
 tests/tcg/multiarch/Makefile.target   |  17 +-
 tests/tcg/multiarch/follow-fork-mode.c|  56 
 .../gdbstub/follow-fork-mode-child.py |  40 +++
 .../gdbstub/follow-fork-mode-parent.py|  16 ++
 37 files changed, 511 insertions(+), 101 deletions(-)
 create mode 100644 tests/tcg/multiarch/follow-fork-mode.c
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py

-- 
2.43.2




[PATCH v4 11/12] gdbstub: Implement follow-fork-mode child

2024-02-19 Thread Ilya Leoshkevich
Currently it's not possible to use gdbstub for debugging linux-user
code that runs in a forked child, which is normally done using the `set
follow-fork-mode child` GDB command. Purely on the protocol level, the
missing piece is the fork-events feature.

However, a deeper problem is supporting $Hg switching between different
processes - right now it can do only threads. Implementing this for the
general case would be quite complicated, but, fortunately, for the
follow-fork-mode case there are a few factors that greatly simplify
things: fork() happens in the exclusive section, there are only two
processes involved, and before one of them is resumed, the second one
is detached.

This makes it possible to implement a simplified scheme: the parent and
the child share the gdbserver socket, it's used only by one of them at
any given time, which is coordinated through a separate socketpair. The
processes can read from the gdbserver socket only one byte at a time,
which is not great for performance, but, fortunately, the
follow-fork-mode handling involves only a few messages.

Advertise the fork-events support, and remember whether GDB has it
as well. Implement the state machine that is initialized on fork(),
decides the current owner of the gdbserver socket, and is terminated
when one of the two processes is detached. The logic for the parent and
the child is the same, only the initial state is different.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c | 212 -
 1 file changed, 210 insertions(+), 2 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 1a7b582a40d..7f9f19a1249 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -25,6 +25,61 @@
 #define GDB_NR_SYSCALLS 1024
 typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)];
 
+/*
+ * Forked child talks to its parent in order to let GDB enforce the
+ * follow-fork-mode. This happens inside a start_exclusive() section, so that
+ * the other threads, which may be forking too, do not interfere. The
+ * implementation relies on GDB not sending $vCont until it has detached
+ * either from the parent (follow-fork-mode child) or from the child
+ * (follow-fork-mode parent).
+ *
+ * The parent and the child share the GDB socket; at any given time only one
+ * of them is allowed to use it, as is reflected in the respective fork_state.
+ * This is negotiated via the fork_sockets pair as a reaction to $Hg.
+ *
+ * Below is a short summary of the possible state transitions:
+ *
+ * ENABLED : Terminal state.
+ * DISABLED: Terminal state.
+ * ACTIVE  : Parent initial state.
+ * INACTIVE: Child initial state.
+ * ACTIVE   -> DEACTIVATING: On $Hg.
+ * ACTIVE   -> ENABLING: On $D.
+ * ACTIVE   -> DISABLING   : On $D.
+ * ACTIVE   -> DISABLED: On communication error.
+ * DEACTIVATING -> INACTIVE: On gdb_read_byte() return.
+ * DEACTIVATING -> DISABLED: On communication error.
+ * INACTIVE -> ACTIVE  : On $Hg in the peer.
+ * INACTIVE -> ENABLE  : On $D in the peer.
+ * INACTIVE -> DISABLE : On $D in the peer.
+ * INACTIVE -> DISABLED: On communication error.
+ * ENABLING -> ENABLED : On gdb_read_byte() return.
+ * ENABLING -> DISABLED: On communication error.
+ * DISABLING-> DISABLED: On gdb_read_byte() return.
+ */
+enum GDBForkState {
+/* Fully owning the GDB socket. */
+GDB_FORK_ENABLED,
+/* Working with the GDB socket; the peer is inactive. */
+GDB_FORK_ACTIVE,
+/* Handing off the GDB socket to the peer. */
+GDB_FORK_DEACTIVATING,
+/* The peer is working with the GDB socket. */
+GDB_FORK_INACTIVE,
+/* Asking the peer to close its GDB socket fd. */
+GDB_FORK_ENABLING,
+/* Asking the peer to take over, closing our GDB socket fd. */
+GDB_FORK_DISABLING,
+/* The peer has taken over, our GDB socket fd is closed. */
+GDB_FORK_DISABLED,
+};
+
+enum GDBForkMessage {
+GDB_FORK_ACTIVATE = 'a',
+GDB_FORK_ENABLE = 'e',
+GDB_FORK_DISABLE = 'd',
+};
+
 /* User-mode specific state */
 typedef struct {
 int fd;
@@ -36,6 +91,10 @@ typedef struct {
  */
 bool catch_all_syscalls;
 GDBSyscallsMask catch_syscalls_mask;
+bool fork_events;
+enum GDBForkState fork_state;
+int fork_sockets[2];
+pid_t fork_peer_pid, fork_peer_tid;
 } GDBUserState;
 
 static GDBUserState gdbserver_user_state;
@@ -358,6 +417,18 @@ int gdbserver_start(const char *port_or_path)
 
 void gdbserver_fork_start(void)
 {
+if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+return;
+}
+if (!gdbserver_user_state.fork_events ||
+qemu_socketpair(AF_UNIX, SOCK_STREAM, 0,
+gdbserver_user_state.fork_sockets) < 

[PATCH v4 09/12] gdbstub: Introduce gdb_handle_set_thread_user()

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support needs to perform certain
actions when GDB switches between the stopped parent and the stopped
child. Introduce a user-specific hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 11 +--
 gdbstub/internals.h |  1 +
 gdbstub/user.c  |  5 +
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 898a108b7f1..05fcdd86527 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1066,6 +1066,7 @@ static void handle_cont_with_sig(GArray *params, void 
*user_ctx)
 
 static void handle_set_thread(GArray *params, void *user_ctx)
 {
+uint32_t pid, tid;
 CPUState *cpu;
 
 if (params->len != 2) {
@@ -1083,8 +1084,14 @@ static void handle_set_thread(GArray *params, void 
*user_ctx)
 return;
 }
 
-cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
-  get_param(params, 1)->thread_id.tid);
+pid = get_param(params, 1)->thread_id.pid;
+tid = get_param(params, 1)->thread_id.tid;
+#ifdef CONFIG_USER_ONLY
+if (gdb_handle_set_thread_user(pid, tid)) {
+return;
+}
+#endif
+cpu = gdb_get_cpu(pid, tid);
 if (!cpu) {
 gdb_put_packet("E22");
 return;
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index e6063835b1f..b4905c7181a 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -197,6 +197,7 @@ void gdb_handle_v_file_readlink(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user 
*/
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
+bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index c9e8b83d720..b048754c4f8 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -386,6 +386,11 @@ void gdb_handle_query_supported_user(const char 
*gdb_supported)
 {
 }
 
+bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid)
+{
+return false;
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.2




Re: [PATCH v3 01/11] gdbstub: Support disablement in a multi-threaded process

2024-02-19 Thread Ilya Leoshkevich
On Mon, 2024-02-19 at 14:05 +0100, Ilya Leoshkevich wrote:
> On Sat, 2024-02-17 at 10:21 -1000, Richard Henderson wrote:
> > On 2/16/24 03:05, Ilya Leoshkevich wrote:
> > > The upcoming follow-fork-mode child support will require
> > > disabling
> > > gdbstub in the parent process, which may have multiple threads
> > > (which
> > > are represented as CPUs).
> > > 
> > > Loop over all CPUs in order to remove breakpoints and disable
> > > single-step. Move the respective code into a separate function.
> > > 
> > > Signed-off-by: Ilya Leoshkevich 
> > > ---
> > >   gdbstub/user.c | 19 +++
> > >   1 file changed, 15 insertions(+), 4 deletions(-)
> > > 
> > > diff --git a/gdbstub/user.c b/gdbstub/user.c
> > > index 14918d1a217..e17f7ece908 100644
> > > --- a/gdbstub/user.c
> > > +++ b/gdbstub/user.c
> > > @@ -356,16 +356,27 @@ int gdbserver_start(const char
> > > *port_or_path)
> > >   return -1;
> > >   }
> > >   
> > > +static void disable_gdbstub(void)
> > > +{
> > > +    CPUState *cpu;
> > > +
> > > +    close(gdbserver_user_state.fd);
> > > +    gdbserver_user_state.fd = -1;
> > > +    CPU_FOREACH(cpu) {
> > > +    cpu_breakpoint_remove_all(cpu, BP_GDB);
> > > +    /* no cpu_watchpoint_remove_all for user-mode */
> > > +    cpu_single_step(cpu, 0);
> > > +    tb_flush(cpu);
> > 
> > You only need to flush once.  The cpu argument is used to determine
> > if we can perform the 
> > flush immediately or need to queue it.
> 
> I thought we needed to flush jump caches on all CPUs, but I see now
> that do_tb_flush() already does this, so this loop is unnecessarily
> quadratic.
> 
> Btw, shouldn't do_tb_flush() have cpu as a local variable, and not as
> a parameter?

Never mind, the dummy parameter is needed for usage with
async_safe_run_on_cpu().


[...]



[PATCH v4 06/12] {linux,bsd}-user: Pass pid to gdbserver_fork()

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing the child
pid. Pass it down.

Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 2 +-
 gdbstub/user.c | 2 +-
 include/gdbstub/user.h | 2 +-
 linux-user/main.c  | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index 94e1edf8247..9c5b5c7e1dc 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -131,7 +131,7 @@ void fork_end(pid_t pid)
  */
 qemu_init_cpu_list();
 get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu);
+gdbserver_fork(thread_cpu, pid);
 } else {
 mmap_fork_end(child);
 cpu_list_unlock();
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 536fb43b03e..c61e1a0d1f6 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -375,7 +375,7 @@ static void disable_gdbstub(CPUState *thread_cpu)
 }
 
 /* Disable gdb stub for child processes.  */
-void gdbserver_fork(CPUState *cpu)
+void gdbserver_fork(CPUState *cpu, pid_t pid)
 {
 if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index e33f8d9a9a6..3f9f45946e0 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -54,7 +54,7 @@ void gdbserver_fork_start(void);
  * gdbserver_fork() - disable gdb stub for child processes.
  * @cs: CPU
  */
-void gdbserver_fork(CPUState *cs);
+void gdbserver_fork(CPUState *cs, pid_t pid);
 
 /**
  * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
diff --git a/linux-user/main.c b/linux-user/main.c
index a490d798898..bc78c1a4b03 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -164,7 +164,7 @@ void fork_end(pid_t pid)
 }
 qemu_init_cpu_list();
 get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu);
+gdbserver_fork(thread_cpu, pid);
 } else {
 cpu_list_unlock();
 }
-- 
2.43.2




[PATCH v4 10/12] gdbstub: Introduce gdb_handle_detach_user()

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support needs to perform certain
actions when GDB detaches from the stopped parent or the stopped child.
Introduce a user-specific hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 6 ++
 gdbstub/internals.h | 1 +
 gdbstub/user.c  | 5 +
 3 files changed, 12 insertions(+)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 05fcdd86527..219b0422f9a 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -991,6 +991,12 @@ static void handle_detach(GArray *params, void *user_ctx)
 pid = get_param(params, 0)->val_ul;
 }
 
+#ifdef CONFIG_USER_ONLY
+if (gdb_handle_detach_user(pid)) {
+return;
+}
+#endif
+
 process = gdb_get_process(pid);
 gdb_process_breakpoint_remove_all(process);
 process->attached = false;
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index b4905c7181a..b4724598384 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -198,6 +198,7 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
 bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */
+bool gdb_handle_detach_user(uint32_t pid); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index b048754c4f8..1a7b582a40d 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -391,6 +391,11 @@ bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid)
 return false;
 }
 
+bool gdb_handle_detach_user(uint32_t pid)
+{
+return false;
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.2




[PATCH v4 12/12] tests/tcg: Add two follow-fork-mode tests

2024-02-19 Thread Ilya Leoshkevich
Add follow-fork-mode child and and follow-fork-mode parent tests.
Check for the obvious pitfalls, such as lingering breakpoints,
catchpoints, and single-step mode.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/Makefile.target   | 17 +-
 tests/tcg/multiarch/follow-fork-mode.c| 56 +++
 .../gdbstub/follow-fork-mode-child.py | 40 +
 .../gdbstub/follow-fork-mode-parent.py| 16 ++
 4 files changed, 128 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/multiarch/follow-fork-mode.c
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py

diff --git a/tests/tcg/multiarch/Makefile.target 
b/tests/tcg/multiarch/Makefile.target
index e10951a8016..b8b70c81860 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -115,6 +115,20 @@ run-gdbstub-catch-syscalls: catch-syscalls
--bin $< --test $(MULTIARCH_SRC)/gdbstub/catch-syscalls.py, \
hitting a syscall catchpoint)
 
+run-gdbstub-follow-fork-mode-child: follow-fork-mode
+   $(call run-test, $@, $(GDB_SCRIPT) \
+   --gdb $(GDB) \
+   --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+   --bin $< --test 
$(MULTIARCH_SRC)/gdbstub/follow-fork-mode-child.py, \
+   following children on fork)
+
+run-gdbstub-follow-fork-mode-parent: follow-fork-mode
+   $(call run-test, $@, $(GDB_SCRIPT) \
+   --gdb $(GDB) \
+   --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+   --bin $< --test 
$(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \
+   following parents on fork)
+
 else
 run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst 
-%,,$(TARGET_NAME)) support")
@@ -122,7 +136,8 @@ endif
 EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
  run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \
  run-gdbstub-registers run-gdbstub-prot-none \
- run-gdbstub-catch-syscalls
+ run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \
+ run-gdbstub-follow-fork-mode-parent
 
 # ARM Compatible Semi Hosting Tests
 #
diff --git a/tests/tcg/multiarch/follow-fork-mode.c 
b/tests/tcg/multiarch/follow-fork-mode.c
new file mode 100644
index 000..cb6b032b388
--- /dev/null
+++ b/tests/tcg/multiarch/follow-fork-mode.c
@@ -0,0 +1,56 @@
+/*
+ * Test GDB's follow-fork-mode.
+ *
+ * fork() a chain of processes.
+ * Parents sends one byte to their children, and children return their
+ * position in the chain, in order to prove that they survived GDB's fork()
+ * handling.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+
+void break_after_fork(void)
+{
+}
+
+int main(void)
+{
+int depth = 42, err, i, fd[2], status;
+pid_t child, pid;
+ssize_t n;
+char b;
+
+for (i = 0; i < depth; i++) {
+err = pipe(fd);
+assert(err == 0);
+child = fork();
+break_after_fork();
+assert(child != -1);
+if (child == 0) {
+close(fd[1]);
+
+n = read(fd[0], , 1);
+close(fd[0]);
+assert(n == 1);
+assert(b == (char)i);
+} else {
+close(fd[0]);
+
+b = (char)i;
+n = write(fd[1], , 1);
+close(fd[1]);
+assert(n == 1);
+
+pid = waitpid(child, , 0);
+assert(pid == child);
+assert(WIFEXITED(status));
+return WEXITSTATUS(status) - 1;
+}
+}
+
+return depth;
+}
diff --git a/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py 
b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
new file mode 100644
index 000..72a6e440c08
--- /dev/null
+++ b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
@@ -0,0 +1,40 @@
+"""Test GDB's follow-fork-mode child.
+
+SPDX-License-Identifier: GPL-2.0-or-later
+"""
+from test_gdbstub import main, report
+
+
+def run_test():
+"""Run through the tests one by one"""
+gdb.execute("set follow-fork-mode child")
+# Check that the parent breakpoints are unset.
+gdb.execute("break break_after_fork")
+# Check that the parent syscall catchpoints are unset.
+# Skip this check on the architectures that don't have them.
+have_fork_syscall = False
+for fork_syscall in ("fork", "clone", "clone2", "clone3"):
+try:
+gdb.execute("catch syscall {}".format(fork_syscall))
+except gdb.error:
+pass
+else:
+have_fork_syscall = True
+gdb.execute("continue")
+for i in range(42):
+

[PATCH v4 07/12] gdbstub: Call gdbserver_fork() both in parent and in child

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires post-fork message
exchange between the parent and the child. Prepare gdbserver_fork() for
this purpose. Rename it to gdbserver_fork_end() to better reflect its
purpose.

Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 3 ++-
 gdbstub/user.c | 5 ++---
 include/gdbstub/user.h | 5 +++--
 linux-user/main.c  | 2 +-
 4 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index 9c5b5c7e1dc..9e23578c4e7 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -131,10 +131,11 @@ void fork_end(pid_t pid)
  */
 qemu_init_cpu_list();
 get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu, pid);
+gdbserver_fork_end(thread_cpu, pid);
 } else {
 mmap_fork_end(child);
 cpu_list_unlock();
+gdbserver_fork_end(thread_cpu, pid);
 end_exclusive();
 }
 }
diff --git a/gdbstub/user.c b/gdbstub/user.c
index c61e1a0d1f6..866a25f9c06 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -374,10 +374,9 @@ static void disable_gdbstub(CPUState *thread_cpu)
 tb_flush(thread_cpu);
 }
 
-/* Disable gdb stub for child processes.  */
-void gdbserver_fork(CPUState *cpu, pid_t pid)
+void gdbserver_fork_end(CPUState *cpu, pid_t pid)
 {
-if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+if (pid != 0 || !gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
 }
 disable_gdbstub(cpu);
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index 3f9f45946e0..4c4e5c4c582 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -51,10 +51,11 @@ void gdb_signalled(CPUArchState *as, int sig);
 void gdbserver_fork_start(void);
 
 /**
- * gdbserver_fork() - disable gdb stub for child processes.
+ * gdbserver_fork_end() - inform gdb of the completed fork()
  * @cs: CPU
+ * @pid: 0 if in child process, -1 if fork failed, child process pid otherwise
  */
-void gdbserver_fork(CPUState *cs, pid_t pid);
+void gdbserver_fork_end(CPUState *cs, pid_t pid);
 
 /**
  * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
diff --git a/linux-user/main.c b/linux-user/main.c
index bc78c1a4b03..b7294b9cee2 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -164,10 +164,10 @@ void fork_end(pid_t pid)
 }
 qemu_init_cpu_list();
 get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu, pid);
 } else {
 cpu_list_unlock();
 }
+gdbserver_fork_end(thread_cpu, pid);
 /*
  * qemu_init_cpu_list() reinitialized the child exclusive state, but we
  * also need to keep current_cpu consistent, so call end_exclusive() for
-- 
2.43.2




[PATCH v4 04/12] gdbstub: Introduce gdbserver_fork_start()

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing when
fork() is about to happen in order to initialize its state. Add a hook
for that.

Reviewed-by: Alex Bennée 
Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 1 +
 gdbstub/user.c | 4 
 include/gdbstub/user.h | 5 +
 linux-user/main.c  | 1 +
 4 files changed, 11 insertions(+)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index 72289673a94..35a27a07c8d 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -106,6 +106,7 @@ void fork_start(void)
 start_exclusive();
 cpu_list_lock();
 mmap_fork_start();
+gdbserver_fork_start();
 }
 
 void fork_end(int child)
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 3ce20b7bbfc..536fb43b03e 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -356,6 +356,10 @@ int gdbserver_start(const char *port_or_path)
 return -1;
 }
 
+void gdbserver_fork_start(void)
+{
+}
+
 static void disable_gdbstub(CPUState *thread_cpu)
 {
 CPUState *cpu;
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index 68b6534130c..e33f8d9a9a6 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -45,6 +45,11 @@ static inline int gdb_handlesig(CPUState *cpu, int sig)
  */
 void gdb_signalled(CPUArchState *as, int sig);
 
+/**
+ * gdbserver_fork_start() - inform gdb of the upcoming fork()
+ */
+void gdbserver_fork_start(void);
+
 /**
  * gdbserver_fork() - disable gdb stub for child processes.
  * @cs: CPU
diff --git a/linux-user/main.c b/linux-user/main.c
index 1d53f708354..dce6233ee49 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -144,6 +144,7 @@ void fork_start(void)
 mmap_fork_start();
 cpu_list_lock();
 qemu_plugin_user_prefork_lock();
+gdbserver_fork_start();
 }
 
 void fork_end(int child)
-- 
2.43.2




[PATCH v4 01/12] gdbstub: Support disablement in a multi-threaded process

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support will require disabling
gdbstub in the parent process, which may have multiple threads (which
are represented as CPUs).

Loop over all CPUs in order to remove breakpoints and disable
single-step. Move the respective code into a separate function.

Reviewed-by: Alex Bennée 
Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c | 19 +++
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 14918d1a217..3ce20b7bbfc 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -356,16 +356,27 @@ int gdbserver_start(const char *port_or_path)
 return -1;
 }
 
+static void disable_gdbstub(CPUState *thread_cpu)
+{
+CPUState *cpu;
+
+close(gdbserver_user_state.fd);
+gdbserver_user_state.fd = -1;
+CPU_FOREACH(cpu) {
+cpu_breakpoint_remove_all(cpu, BP_GDB);
+/* no cpu_watchpoint_remove_all for user-mode */
+cpu_single_step(cpu, 0);
+}
+tb_flush(thread_cpu);
+}
+
 /* Disable gdb stub for child processes.  */
 void gdbserver_fork(CPUState *cpu)
 {
 if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
 }
-close(gdbserver_user_state.fd);
-gdbserver_user_state.fd = -1;
-cpu_breakpoint_remove_all(cpu, BP_GDB);
-/* no cpu_watchpoint_remove_all for user-mode */
+disable_gdbstub(cpu);
 }
 
 /*
-- 
2.43.2




[PATCH v4 05/12] {linux,bsd}-user: Pass pid to fork_end()

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing the child
pid. Pass it down.

Reviewed-by: Alex Bennée 
Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/freebsd/os-proc.h  | 6 +++---
 bsd-user/main.c | 4 +++-
 bsd-user/qemu.h | 2 +-
 linux-user/main.c   | 4 +++-
 linux-user/syscall.c| 6 +++---
 linux-user/user-internals.h | 2 +-
 6 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h
index d6418780344..3003c8cb637 100644
--- a/bsd-user/freebsd/os-proc.h
+++ b/bsd-user/freebsd/os-proc.h
@@ -208,7 +208,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
  */
 set_second_rval(cpu_env, child_flag);
 
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 }
@@ -252,7 +252,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, 
abi_long flags)
  * value: 0 for parent process, 1 for child process.
  */
 set_second_rval(cpu_env, child_flag);
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 
@@ -285,7 +285,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, 
abi_ulong target_fdp,
  * value: 0 for parent process, 1 for child process.
  */
 set_second_rval(cpu_env, child_flag);
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 }
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 35a27a07c8d..94e1edf8247 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -109,8 +109,10 @@ void fork_start(void)
 gdbserver_fork_start();
 }
 
-void fork_end(int child)
+void fork_end(pid_t pid)
 {
+bool child = pid == 0;
+
 if (child) {
 CPUState *cpu, *next_cpu;
 /*
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index a2417b25156..c1ee90a296d 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -185,7 +185,7 @@ void cpu_loop(CPUArchState *env);
 char *target_strerror(int err);
 int get_osversion(void);
 void fork_start(void);
-void fork_end(int child);
+void fork_end(pid_t pid);
 
 #include "qemu/log.h"
 
diff --git a/linux-user/main.c b/linux-user/main.c
index dce6233ee49..a490d798898 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -147,8 +147,10 @@ void fork_start(void)
 gdbserver_fork_start();
 }
 
-void fork_end(int child)
+void fork_end(pid_t pid)
 {
+bool child = pid == 0;
+
 qemu_plugin_user_postfork(child);
 mmap_fork_end(child);
 if (child) {
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 3e105488ee2..a0b61bcb2a2 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -6669,7 +6669,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, 
abi_ulong newsp,
 if (ret == 0) {
 /* Child Process.  */
 cpu_clone_regs_child(env, newsp, flags);
-fork_end(1);
+fork_end(ret);
 /* There is a race condition here.  The parent process could
theoretically read the TID in the child process before the child
tid is set.  This would require using either ptrace
@@ -6700,8 +6700,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, 
abi_ulong newsp,
 }
 #endif
 put_user_u32(pid_fd, parent_tidptr);
-}
-fork_end(0);
+}
+fork_end(ret);
 }
 g_assert(!cpu_in_exclusive_context(cpu));
 }
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index c63ef45fc78..ce11d9e21c1 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -71,7 +71,7 @@ const char *target_strerror(int err);
 int get_osversion(void);
 void init_qemu_uname_release(void);
 void fork_start(void);
-void fork_end(int child);
+void fork_end(pid_t pid);
 
 /**
  * probe_guest_base:
-- 
2.43.2




[PATCH v4 08/12] gdbstub: Introduce gdb_handle_query_supported_user()

2024-02-19 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires advertising the
fork-events feature, which is user-specific. Introduce a user-specific
hook for this.

Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 12 +---
 gdbstub/internals.h |  1 +
 gdbstub/user.c  |  4 
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 7e73e916bdc..898a108b7f1 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1622,9 +1622,15 @@ static void handle_query_supported(GArray *params, void 
*user_ctx)
 g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+");
 #endif
 
-if (params->len &&
-strstr(get_param(params, 0)->data, "multiprocess+")) {
-gdbserver_state.multiprocess = true;
+if (params->len) {
+const char *gdb_supported = get_param(params, 0)->data;
+
+if (strstr(gdb_supported, "multiprocess+")) {
+gdbserver_state.multiprocess = true;
+}
+#if defined(CONFIG_USER_ONLY)
+gdb_handle_query_supported_user(gdb_supported);
+#endif
 }
 
 g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index 56b7c13b750..e6063835b1f 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -196,6 +196,7 @@ void gdb_handle_v_file_pread(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user 
*/
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
+void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 866a25f9c06..c9e8b83d720 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -382,6 +382,10 @@ void gdbserver_fork_end(CPUState *cpu, pid_t pid)
 disable_gdbstub(cpu);
 }
 
+void gdb_handle_query_supported_user(const char *gdb_supported)
+{
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.2




Re: [PATCH v3 01/11] gdbstub: Support disablement in a multi-threaded process

2024-02-19 Thread Ilya Leoshkevich
On Sat, 2024-02-17 at 10:21 -1000, Richard Henderson wrote:
> On 2/16/24 03:05, Ilya Leoshkevich wrote:
> > The upcoming follow-fork-mode child support will require disabling
> > gdbstub in the parent process, which may have multiple threads
> > (which
> > are represented as CPUs).
> > 
> > Loop over all CPUs in order to remove breakpoints and disable
> > single-step. Move the respective code into a separate function.
> > 
> > Signed-off-by: Ilya Leoshkevich 
> > ---
> >   gdbstub/user.c | 19 +++
> >   1 file changed, 15 insertions(+), 4 deletions(-)
> > 
> > diff --git a/gdbstub/user.c b/gdbstub/user.c
> > index 14918d1a217..e17f7ece908 100644
> > --- a/gdbstub/user.c
> > +++ b/gdbstub/user.c
> > @@ -356,16 +356,27 @@ int gdbserver_start(const char *port_or_path)
> >   return -1;
> >   }
> >   
> > +static void disable_gdbstub(void)
> > +{
> > +    CPUState *cpu;
> > +
> > +    close(gdbserver_user_state.fd);
> > +    gdbserver_user_state.fd = -1;
> > +    CPU_FOREACH(cpu) {
> > +    cpu_breakpoint_remove_all(cpu, BP_GDB);
> > +    /* no cpu_watchpoint_remove_all for user-mode */
> > +    cpu_single_step(cpu, 0);
> > +    tb_flush(cpu);
> 
> You only need to flush once.  The cpu argument is used to determine
> if we can perform the 
> flush immediately or need to queue it.

I thought we needed to flush jump caches on all CPUs, but I see now
that do_tb_flush() already does this, so this loop is unnecessarily
quadratic.

Btw, shouldn't do_tb_flush() have cpu as a local variable, and not as
a parameter?

> 
> Otherwise,
> Reviewed-by: Richard Henderson 
> 
> 
> r~




[PATCH v3 10/11] gdbstub: Implement follow-fork-mode child

2024-02-16 Thread Ilya Leoshkevich
Currently it's not possible to use gdbstub for debugging linux-user
code that runs in a forked child, which is normally done using the `set
follow-fork-mode child` GDB command. Purely on the protocol level, the
missing piece is the fork-events feature.

However, a deeper problem is supporting $Hg switching between different
processes - right now it can do only threads. Implementing this for the
general case would be quite complicated, but, fortunately, for the
follow-fork-mode case there are a few factors that greatly simplify
things: fork() happens in the exclusive section, there are only two
processes involved, and before one of them is resumed, the second one
is detached.

This makes it possible to implement a simplified scheme: the parent and
the child share the gdbserver socket, it's used only by one of them at
any given time, which is coordinated through a separate socketpair. The
processes can read from the gdbserver socket only one byte at a time,
which is not great for performance, but, fortunately, the
follow-fork-mode handling involves only a few messages.

Advertise the fork-events support, and remember whether GDB has it
as well. Implement the state machine that is initialized on fork(),
decides the current owner of the gdbserver socket, and is terminated
when one of the two processes is detached. The logic for the parent and
the child is the same, only the initial state is different.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c | 212 -
 1 file changed, 210 insertions(+), 2 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 32518f275f5..7dcc1159cc0 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -25,6 +25,61 @@
 #define GDB_NR_SYSCALLS 1024
 typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)];
 
+/*
+ * Forked child talks to its parent in order to let GDB enforce the
+ * follow-fork-mode. This happens inside a start_exclusive() section, so that
+ * the other threads, which may be forking too, do not interfere. The
+ * implementation relies on GDB not sending $vCont until it has detached
+ * either from the parent (follow-fork-mode child) or from the child
+ * (follow-fork-mode parent).
+ *
+ * The parent and the child share the GDB socket; at any given time only one
+ * of them is allowed to use it, as is reflected in the respective fork_state.
+ * This is negotiated via the fork_sockets pair as a reaction to $Hg.
+ *
+ * Below is a short summary of the possible state transitions:
+ *
+ * ENABLED : Terminal state.
+ * DISABLED: Terminal state.
+ * ACTIVE  : Parent initial state.
+ * INACTIVE: Child initial state.
+ * ACTIVE   -> DEACTIVATING: On $Hg.
+ * ACTIVE   -> ENABLING: On $D.
+ * ACTIVE   -> DISABLING   : On $D.
+ * ACTIVE   -> DISABLED: On communication error.
+ * DEACTIVATING -> INACTIVE: On gdb_read_byte() return.
+ * DEACTIVATING -> DISABLED: On communication error.
+ * INACTIVE -> ACTIVE  : On $Hg in the peer.
+ * INACTIVE -> ENABLE  : On $D in the peer.
+ * INACTIVE -> DISABLE : On $D in the peer.
+ * INACTIVE -> DISABLED: On communication error.
+ * ENABLING -> ENABLED : On gdb_read_byte() return.
+ * ENABLING -> DISABLED: On communication error.
+ * DISABLING-> DISABLED: On gdb_read_byte() return.
+ */
+enum GDBForkState {
+/* Fully owning the GDB socket. */
+GDB_FORK_ENABLED,
+/* Working with the GDB socket; the peer is inactive. */
+GDB_FORK_ACTIVE,
+/* Handing off the GDB socket to the peer. */
+GDB_FORK_DEACTIVATING,
+/* The peer is working with the GDB socket. */
+GDB_FORK_INACTIVE,
+/* Asking the peer to close its GDB socket fd. */
+GDB_FORK_ENABLING,
+/* Asking the peer to take over, closing our GDB socket fd. */
+GDB_FORK_DISABLING,
+/* The peer has taken over, our GDB socket fd is closed. */
+GDB_FORK_DISABLED,
+};
+
+enum GDBForkMessage {
+GDB_FORK_ACTIVATE = 'a',
+GDB_FORK_ENABLE = 'e',
+GDB_FORK_DISABLE = 'd',
+};
+
 /* User-mode specific state */
 typedef struct {
 int fd;
@@ -36,6 +91,10 @@ typedef struct {
  */
 bool catch_all_syscalls;
 GDBSyscallsMask catch_syscalls_mask;
+bool fork_events;
+enum GDBForkState fork_state;
+int fork_sockets[2];
+pid_t fork_peer_pid, fork_peer_tid;
 } GDBUserState;
 
 static GDBUserState gdbserver_user_state;
@@ -358,6 +417,18 @@ int gdbserver_start(const char *port_or_path)
 
 void gdbserver_fork_start(void)
 {
+if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+return;
+}
+if (!gdbserver_user_state.fork_events ||
+qemu_socketpair(AF_UNIX, SOCK_STREAM, 0,
+gdbserver_user_state.fork_sockets) < 

[PATCH v3 11/11] tests/tcg: Add two follow-fork-mode tests

2024-02-16 Thread Ilya Leoshkevich
Add follow-fork-mode child and and follow-fork-mode parent tests.
Check for the obvious pitfalls, such as lingering breakpoints,
catchpoints, and single-step mode.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/Makefile.target   | 17 +-
 tests/tcg/multiarch/follow-fork-mode.c| 56 +++
 .../gdbstub/follow-fork-mode-child.py | 40 +
 .../gdbstub/follow-fork-mode-parent.py| 16 ++
 4 files changed, 128 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/multiarch/follow-fork-mode.c
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py

diff --git a/tests/tcg/multiarch/Makefile.target 
b/tests/tcg/multiarch/Makefile.target
index e10951a8016..b8b70c81860 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -115,6 +115,20 @@ run-gdbstub-catch-syscalls: catch-syscalls
--bin $< --test $(MULTIARCH_SRC)/gdbstub/catch-syscalls.py, \
hitting a syscall catchpoint)
 
+run-gdbstub-follow-fork-mode-child: follow-fork-mode
+   $(call run-test, $@, $(GDB_SCRIPT) \
+   --gdb $(GDB) \
+   --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+   --bin $< --test 
$(MULTIARCH_SRC)/gdbstub/follow-fork-mode-child.py, \
+   following children on fork)
+
+run-gdbstub-follow-fork-mode-parent: follow-fork-mode
+   $(call run-test, $@, $(GDB_SCRIPT) \
+   --gdb $(GDB) \
+   --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+   --bin $< --test 
$(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \
+   following parents on fork)
+
 else
 run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst 
-%,,$(TARGET_NAME)) support")
@@ -122,7 +136,8 @@ endif
 EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
  run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \
  run-gdbstub-registers run-gdbstub-prot-none \
- run-gdbstub-catch-syscalls
+ run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \
+ run-gdbstub-follow-fork-mode-parent
 
 # ARM Compatible Semi Hosting Tests
 #
diff --git a/tests/tcg/multiarch/follow-fork-mode.c 
b/tests/tcg/multiarch/follow-fork-mode.c
new file mode 100644
index 000..cb6b032b388
--- /dev/null
+++ b/tests/tcg/multiarch/follow-fork-mode.c
@@ -0,0 +1,56 @@
+/*
+ * Test GDB's follow-fork-mode.
+ *
+ * fork() a chain of processes.
+ * Parents sends one byte to their children, and children return their
+ * position in the chain, in order to prove that they survived GDB's fork()
+ * handling.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+
+void break_after_fork(void)
+{
+}
+
+int main(void)
+{
+int depth = 42, err, i, fd[2], status;
+pid_t child, pid;
+ssize_t n;
+char b;
+
+for (i = 0; i < depth; i++) {
+err = pipe(fd);
+assert(err == 0);
+child = fork();
+break_after_fork();
+assert(child != -1);
+if (child == 0) {
+close(fd[1]);
+
+n = read(fd[0], , 1);
+close(fd[0]);
+assert(n == 1);
+assert(b == (char)i);
+} else {
+close(fd[0]);
+
+b = (char)i;
+n = write(fd[1], , 1);
+close(fd[1]);
+assert(n == 1);
+
+pid = waitpid(child, , 0);
+assert(pid == child);
+assert(WIFEXITED(status));
+return WEXITSTATUS(status) - 1;
+}
+}
+
+return depth;
+}
diff --git a/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py 
b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
new file mode 100644
index 000..72a6e440c08
--- /dev/null
+++ b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
@@ -0,0 +1,40 @@
+"""Test GDB's follow-fork-mode child.
+
+SPDX-License-Identifier: GPL-2.0-or-later
+"""
+from test_gdbstub import main, report
+
+
+def run_test():
+"""Run through the tests one by one"""
+gdb.execute("set follow-fork-mode child")
+# Check that the parent breakpoints are unset.
+gdb.execute("break break_after_fork")
+# Check that the parent syscall catchpoints are unset.
+# Skip this check on the architectures that don't have them.
+have_fork_syscall = False
+for fork_syscall in ("fork", "clone", "clone2", "clone3"):
+try:
+gdb.execute("catch syscall {}".format(fork_syscall))
+except gdb.error:
+pass
+else:
+have_fork_syscall = True
+gdb.execute("continue")
+for i in range(42):
+

[PATCH v3 06/11] gdbstub: Call gdbserver_fork() both in parent and in child

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires post-fork message
exchange between the parent and the child. Prepare gdbserver_fork() for
this purpose. Rename it to gdbserver_fork_end() to better reflect its
purpose.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 3 ++-
 gdbstub/user.c | 5 ++---
 include/gdbstub/user.h | 6 +++---
 linux-user/main.c  | 2 +-
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index e8c658eda5d..1890d6365f7 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -131,10 +131,11 @@ void fork_end(abi_long pid)
  */
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(pid);
+gdbserver_fork_end(pid);
 } else {
 mmap_fork_end(child);
 cpu_list_unlock();
+gdbserver_fork_end(pid);
 end_exclusive();
 }
 }
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 69b9857e5b6..6ac9b684427 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -374,10 +374,9 @@ static void disable_gdbstub(void)
 }
 }
 
-/* Disable gdb stub for child processes.  */
-void gdbserver_fork(pid_t pid)
+void gdbserver_fork_end(pid_t pid)
 {
-if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+if (pid != 0 || !gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
 }
 disable_gdbstub();
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index 66dd0d319cf..dd03dbdd6df 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -51,10 +51,10 @@ void gdb_signalled(CPUArchState *as, int sig);
 void gdbserver_fork_start(void);
 
 /**
- * gdbserver_fork() - disable gdb stub for child processes.
- * @cs: CPU
+ * gdbserver_fork_end() - inform gdb of the completed fork()
+ * @pid: 0 if in child process, -1 if fork failed, child process pid otherwise
  */
-void gdbserver_fork(pid_t pid);
+void gdbserver_fork_end(pid_t pid);
 
 /**
  * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
diff --git a/linux-user/main.c b/linux-user/main.c
index ad1c6394520..dde5081e2f4 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -164,10 +164,10 @@ void fork_end(abi_long pid)
 }
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(pid);
 } else {
 cpu_list_unlock();
 }
+gdbserver_fork_end(pid);
 /*
  * qemu_init_cpu_list() reinitialized the child exclusive state, but we
  * also need to keep current_cpu consistent, so call end_exclusive() for
-- 
2.43.0




[PATCH v3 08/11] gdbstub: Introduce gdb_handle_set_thread_user()

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support needs to perform certain
actions when GDB switches between the stopped parent and the stopped
child. Introduce a user-specific hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 11 +--
 gdbstub/internals.h |  1 +
 gdbstub/user.c  |  5 +
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 43d79dfdd59..adcd977cd57 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1066,6 +1066,7 @@ static void handle_cont_with_sig(GArray *params, void 
*user_ctx)
 
 static void handle_set_thread(GArray *params, void *user_ctx)
 {
+uint32_t pid, tid;
 CPUState *cpu;
 
 if (params->len != 2) {
@@ -1083,8 +1084,14 @@ static void handle_set_thread(GArray *params, void 
*user_ctx)
 return;
 }
 
-cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
-  get_param(params, 1)->thread_id.tid);
+pid = get_param(params, 1)->thread_id.pid;
+tid = get_param(params, 1)->thread_id.tid;
+#ifdef CONFIG_USER_ONLY
+if (gdb_handle_set_thread_user(pid, tid)) {
+return;
+}
+#endif
+cpu = gdb_get_cpu(pid, tid);
 if (!cpu) {
 gdb_put_packet("E22");
 return;
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index e6063835b1f..b4905c7181a 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -197,6 +197,7 @@ void gdb_handle_v_file_readlink(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user 
*/
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
+bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index d8db5bd3949..60f3e83849e 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -386,6 +386,11 @@ void gdb_handle_query_supported_user(const char 
*gdb_supported)
 {
 }
 
+bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid)
+{
+return false;
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.0




[PATCH v3 04/11] {linux,bsd}-user: Pass pid to fork_end()

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing the child
pid. Pass it down.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/freebsd/os-proc.h  | 6 +++---
 bsd-user/main.c | 4 +++-
 bsd-user/qemu.h | 2 +-
 linux-user/main.c   | 4 +++-
 linux-user/syscall.c| 6 +++---
 linux-user/user-internals.h | 2 +-
 6 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h
index d6418780344..3003c8cb637 100644
--- a/bsd-user/freebsd/os-proc.h
+++ b/bsd-user/freebsd/os-proc.h
@@ -208,7 +208,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
  */
 set_second_rval(cpu_env, child_flag);
 
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 }
@@ -252,7 +252,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, 
abi_long flags)
  * value: 0 for parent process, 1 for child process.
  */
 set_second_rval(cpu_env, child_flag);
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 
@@ -285,7 +285,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, 
abi_ulong target_fdp,
  * value: 0 for parent process, 1 for child process.
  */
 set_second_rval(cpu_env, child_flag);
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 }
diff --git a/bsd-user/main.c b/bsd-user/main.c
index bfe6888ea89..bc233a85cef 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -109,8 +109,10 @@ void fork_start(void)
 gdbserver_fork_start();
 }
 
-void fork_end(int child)
+void fork_end(abi_long pid)
 {
+int child = pid == 0;
+
 if (child) {
 CPUState *cpu, *next_cpu;
 /*
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index dc842fffa7d..2414a87559b 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -180,7 +180,7 @@ void cpu_loop(CPUArchState *env);
 char *target_strerror(int err);
 int get_osversion(void);
 void fork_start(void);
-void fork_end(int child);
+void fork_end(abi_long pid);
 
 #include "qemu/log.h"
 
diff --git a/linux-user/main.c b/linux-user/main.c
index 8c7bea1c631..f1a0267816b 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -147,8 +147,10 @@ void fork_start(void)
 gdbserver_fork_start();
 }
 
-void fork_end(int child)
+void fork_end(abi_long pid)
 {
+int child = pid == 0;
+
 qemu_plugin_user_postfork(child);
 mmap_fork_end(child);
 if (child) {
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index e384e142489..572d8e0ed1c 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -6669,7 +6669,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, 
abi_ulong newsp,
 if (ret == 0) {
 /* Child Process.  */
 cpu_clone_regs_child(env, newsp, flags);
-fork_end(1);
+fork_end(ret);
 /* There is a race condition here.  The parent process could
theoretically read the TID in the child process before the child
tid is set.  This would require using either ptrace
@@ -6700,8 +6700,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, 
abi_ulong newsp,
 }
 #endif
 put_user_u32(pid_fd, parent_tidptr);
-}
-fork_end(0);
+}
+fork_end(ret);
 }
 g_assert(!cpu_in_exclusive_context(cpu));
 }
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index c63ef45fc78..9014014d920 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -71,7 +71,7 @@ const char *target_strerror(int err);
 int get_osversion(void);
 void init_qemu_uname_release(void);
 void fork_start(void);
-void fork_end(int child);
+void fork_end(abi_long pid);
 
 /**
  * probe_guest_base:
-- 
2.43.0




[PATCH v3 09/11] gdbstub: Introduce gdb_handle_detach_user()

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support needs to perform certain
actions when GDB detaches from the stopped parent or the stopped child.
Introduce a user-specific hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 6 ++
 gdbstub/internals.h | 1 +
 gdbstub/user.c  | 5 +
 3 files changed, 12 insertions(+)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index adcd977cd57..46f5dd47e9e 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -991,6 +991,12 @@ static void handle_detach(GArray *params, void *user_ctx)
 pid = get_param(params, 0)->val_ul;
 }
 
+#ifdef CONFIG_USER_ONLY
+if (gdb_handle_detach_user(pid)) {
+return;
+}
+#endif
+
 process = gdb_get_process(pid);
 gdb_process_breakpoint_remove_all(process);
 process->attached = false;
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index b4905c7181a..b4724598384 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -198,6 +198,7 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
 bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */
+bool gdb_handle_detach_user(uint32_t pid); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 60f3e83849e..32518f275f5 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -391,6 +391,11 @@ bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid)
 return false;
 }
 
+bool gdb_handle_detach_user(uint32_t pid)
+{
+return false;
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.0




[PATCH v3 00/11] gdbstub: Implement follow-fork-mode child

2024-02-16 Thread Ilya Leoshkevich
v2: https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg00810.html
v2 -> v3: Rebase on top of master.
  Fix a typo in the 01/11 commit message.

v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06646.html
v1 -> v2: Factor out a number of prep patches;
  Add a state transition diagram comment (Alex).
  Improve a few comments;
  Extend the ts_tid fix to bsd.

Hi,

I needed to debug a linux-user crash between fork() and exec() [1] and
realized that gdbstub does not allow this. This series lifts this
restriction (one still cannot debug past exec() though). Patches 1-9
are preliminary refactorings, patch 10 is the implementation, and patch
11 is the test.

[1] https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06424.html

Best regards,
Ilya

Ilya Leoshkevich (11):
  gdbstub: Support disablement in a multi-threaded process
  {linux,bsd}-user: Update ts_tid after fork()
  gdbstub: Introduce gdbserver_fork_start()
  {linux,bsd}-user: Pass pid to fork_end()
  {linux,bsd}-user: Pass pid to gdbserver_fork()
  gdbstub: Call gdbserver_fork() both in parent and in child
  gdbstub: Introduce gdb_handle_query_supported_user()
  gdbstub: Introduce gdb_handle_set_thread_user()
  gdbstub: Introduce gdb_handle_detach_user()
  gdbstub: Implement follow-fork-mode child
  tests/tcg: Add two follow-fork-mode tests

 bsd-user/freebsd/os-proc.h|   6 +-
 bsd-user/main.c   |   9 +-
 bsd-user/qemu.h   |   2 +-
 gdbstub/gdbstub.c |  29 ++-
 gdbstub/internals.h   |   3 +
 gdbstub/user.c| 244 +-
 include/gdbstub/user.h|  11 +-
 linux-user/main.c |   8 +-
 linux-user/syscall.c  |   6 +-
 linux-user/user-internals.h   |   2 +-
 tests/tcg/multiarch/Makefile.target   |  17 +-
 tests/tcg/multiarch/follow-fork-mode.c|  56 
 .../gdbstub/follow-fork-mode-child.py |  40 +++
 .../gdbstub/follow-fork-mode-parent.py|  16 ++
 14 files changed, 424 insertions(+), 25 deletions(-)
 create mode 100644 tests/tcg/multiarch/follow-fork-mode.c
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py

-- 
2.43.0




[PATCH v3 05/11] {linux,bsd}-user: Pass pid to gdbserver_fork()

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing the child
pid. Pass it down.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 2 +-
 gdbstub/user.c | 2 +-
 include/gdbstub/user.h | 2 +-
 linux-user/main.c  | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index bc233a85cef..e8c658eda5d 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -131,7 +131,7 @@ void fork_end(abi_long pid)
  */
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu);
+gdbserver_fork(pid);
 } else {
 mmap_fork_end(child);
 cpu_list_unlock();
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 5024c670f85..69b9857e5b6 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -375,7 +375,7 @@ static void disable_gdbstub(void)
 }
 
 /* Disable gdb stub for child processes.  */
-void gdbserver_fork(CPUState *cpu)
+void gdbserver_fork(pid_t pid)
 {
 if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index e33f8d9a9a6..66dd0d319cf 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -54,7 +54,7 @@ void gdbserver_fork_start(void);
  * gdbserver_fork() - disable gdb stub for child processes.
  * @cs: CPU
  */
-void gdbserver_fork(CPUState *cs);
+void gdbserver_fork(pid_t pid);
 
 /**
  * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
diff --git a/linux-user/main.c b/linux-user/main.c
index f1a0267816b..ad1c6394520 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -164,7 +164,7 @@ void fork_end(abi_long pid)
 }
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu);
+gdbserver_fork(pid);
 } else {
 cpu_list_unlock();
 }
-- 
2.43.0




[PATCH v3 03/11] gdbstub: Introduce gdbserver_fork_start()

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing when
fork() is about to happen in order to initialize its state. Add a hook
for that.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 1 +
 gdbstub/user.c | 4 
 include/gdbstub/user.h | 5 +
 linux-user/main.c  | 1 +
 4 files changed, 11 insertions(+)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index 4140edc8311..bfe6888ea89 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -106,6 +106,7 @@ void fork_start(void)
 start_exclusive();
 cpu_list_lock();
 mmap_fork_start();
+gdbserver_fork_start();
 }
 
 void fork_end(int child)
diff --git a/gdbstub/user.c b/gdbstub/user.c
index e17f7ece908..5024c670f85 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -356,6 +356,10 @@ int gdbserver_start(const char *port_or_path)
 return -1;
 }
 
+void gdbserver_fork_start(void)
+{
+}
+
 static void disable_gdbstub(void)
 {
 CPUState *cpu;
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index 68b6534130c..e33f8d9a9a6 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -45,6 +45,11 @@ static inline int gdb_handlesig(CPUState *cpu, int sig)
  */
 void gdb_signalled(CPUArchState *as, int sig);
 
+/**
+ * gdbserver_fork_start() - inform gdb of the upcoming fork()
+ */
+void gdbserver_fork_start(void);
+
 /**
  * gdbserver_fork() - disable gdb stub for child processes.
  * @cs: CPU
diff --git a/linux-user/main.c b/linux-user/main.c
index e6427d72332..8c7bea1c631 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -144,6 +144,7 @@ void fork_start(void)
 mmap_fork_start();
 cpu_list_lock();
 qemu_plugin_user_prefork_lock();
+gdbserver_fork_start();
 }
 
 void fork_end(int child)
-- 
2.43.0




[PATCH v3 01/11] gdbstub: Support disablement in a multi-threaded process

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support will require disabling
gdbstub in the parent process, which may have multiple threads (which
are represented as CPUs).

Loop over all CPUs in order to remove breakpoints and disable
single-step. Move the respective code into a separate function.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c | 19 +++
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 14918d1a217..e17f7ece908 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -356,16 +356,27 @@ int gdbserver_start(const char *port_or_path)
 return -1;
 }
 
+static void disable_gdbstub(void)
+{
+CPUState *cpu;
+
+close(gdbserver_user_state.fd);
+gdbserver_user_state.fd = -1;
+CPU_FOREACH(cpu) {
+cpu_breakpoint_remove_all(cpu, BP_GDB);
+/* no cpu_watchpoint_remove_all for user-mode */
+cpu_single_step(cpu, 0);
+tb_flush(cpu);
+}
+}
+
 /* Disable gdb stub for child processes.  */
 void gdbserver_fork(CPUState *cpu)
 {
 if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
 }
-close(gdbserver_user_state.fd);
-gdbserver_user_state.fd = -1;
-cpu_breakpoint_remove_all(cpu, BP_GDB);
-/* no cpu_watchpoint_remove_all for user-mode */
+disable_gdbstub();
 }
 
 /*
-- 
2.43.0




[PATCH v3 07/11] gdbstub: Introduce gdb_handle_query_supported_user()

2024-02-16 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires advertising the
fork-events feature, which is user-specific. Introduce a user-specific
hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 12 +---
 gdbstub/internals.h |  1 +
 gdbstub/user.c  |  4 
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 7e73e916bdc..43d79dfdd59 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1599,6 +1599,7 @@ static void handle_query_thread_extra(GArray *params, 
void *user_ctx)
 
 static void handle_query_supported(GArray *params, void *user_ctx)
 {
+const char *gdb_supported;
 CPUClass *cc;
 
 g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", 
MAX_PACKET_LENGTH);
@@ -1622,9 +1623,14 @@ static void handle_query_supported(GArray *params, void 
*user_ctx)
 g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+");
 #endif
 
-if (params->len &&
-strstr(get_param(params, 0)->data, "multiprocess+")) {
-gdbserver_state.multiprocess = true;
+if (params->len) {
+gdb_supported = get_param(params, 0)->data;
+if (strstr(gdb_supported, "multiprocess+")) {
+gdbserver_state.multiprocess = true;
+}
+#if defined(CONFIG_USER_ONLY)
+gdb_handle_query_supported_user(gdb_supported);
+#endif
 }
 
 g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index 56b7c13b750..e6063835b1f 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -196,6 +196,7 @@ void gdb_handle_v_file_pread(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user 
*/
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
+void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 6ac9b684427..d8db5bd3949 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -382,6 +382,10 @@ void gdbserver_fork_end(pid_t pid)
 disable_gdbstub();
 }
 
+void gdb_handle_query_supported_user(const char *gdb_supported)
+{
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.0




[PATCH v3 02/11] {linux,bsd}-user: Update ts_tid after fork()

2024-02-16 Thread Ilya Leoshkevich
Currently ts_tid contains the parent tid after fork(), which is not
correct. So far it has not affected anything, but the upcoming
follow-fork-mode child support relies on the correct value, so fix it.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c   | 1 +
 linux-user/main.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index e5efb7b8458..4140edc8311 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -127,6 +127,7 @@ void fork_end(int child)
  * state, so we don't need to end_exclusive() here.
  */
 qemu_init_cpu_list();
+((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
 gdbserver_fork(thread_cpu);
 } else {
 mmap_fork_end(child);
diff --git a/linux-user/main.c b/linux-user/main.c
index 74b2fbb3938..e6427d72332 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -160,6 +160,7 @@ void fork_end(int child)
 }
 }
 qemu_init_cpu_list();
+((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
 gdbserver_fork(thread_cpu);
 } else {
 cpu_list_unlock();
-- 
2.43.0




Re: [PATCH v5 0/3] linux-user: Allow gdbstub to ignore page protection

2024-02-16 Thread Ilya Leoshkevich
On Fri, 2024-02-16 at 11:44 +, Alex Bennée wrote:
> Ilya Leoshkevich  writes:
> 
> > v4:
> > https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg05857.html
> > v4 -> v5: Fix the probe_proc_self_mem vs probe_proc_self_mem()
> > typo.
> >   Add Alex's R-b and A-b.
> 
> I was going to pull this and realised it already went in via
> Richard's
> tcg-next. Did this fix get merged?
> 

Hi, yes, I submitted it separately and it was merged as:

commit da4038d2da6d3a3d5f86665bd51b2ba49df5d652
Author: Ilya Leoshkevich 
Date:   Wed Jan 31 23:02:18 2024 +0100

tests/tcg: Fix the /proc/self/mem probing in the PROT_NONE gdbstub
test





Re: [PATCH v3 22/33] linux-user: Split out mmap_h_lt_g

2024-02-14 Thread Ilya Leoshkevich
On Tue, 2024-02-13 at 09:54 -1000, Richard Henderson wrote:
> On 1/29/24 05:26, Ilya Leoshkevich wrote:
> > On Tue, Jan 02, 2024 at 12:57:57PM +1100, Richard Henderson wrote:
> > > Work much harder to get alignment and mapping beyond the end
> > > of the file correct.  Both of which are excercised by our
> > > test-mmap for alpha (8k pages) on any 4k page host.
> > > 
> > > Signed-off-by: Richard Henderson 
> > > ---
> > >   linux-user/mmap.c | 156 +--
> > > ---
> > >   1 file changed, 125 insertions(+), 31 deletions(-)
> > 
> > [...]
> > 
> > > +    if (fileend_adj) {
> > > +    void *t = mmap(p, len - fileend_adj, host_prot,
> > > +   (flags & ~MAP_FIXED_NOREPLACE) |
> > > MAP_FIXED,
> > > +   fd, offset);
> > > +    assert(t != MAP_FAILED);
> > 
> > Is it possible to recover here? Of course, we are remapping the
> > memory
> > we've mapped a few lines earlier, but asserting the syscall result
> > looks a bit odd.
> > 
> 
> Can you think of a failure mode?  I couldn't.
> That's why I added the assert.
> 
> I suppose there's the always present threat of running out of vmas...

Right, and this should be easy to trigger by using ulimit -v.

> 
> 
> r~
> 




[PATCH v2 2/2] tests/tcg: Add SIGRTMIN/SIGRTMAX test

2024-02-12 Thread Ilya Leoshkevich
Test the lowest and the highest real-time signals.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/linux/linux-sigrtminmax.c | 41 +++
 1 file changed, 41 insertions(+)
 create mode 100644 tests/tcg/multiarch/linux/linux-sigrtminmax.c

diff --git a/tests/tcg/multiarch/linux/linux-sigrtminmax.c 
b/tests/tcg/multiarch/linux/linux-sigrtminmax.c
new file mode 100644
index 000..773383a3fef
--- /dev/null
+++ b/tests/tcg/multiarch/linux/linux-sigrtminmax.c
@@ -0,0 +1,41 @@
+/*
+ * Test the lowest and the highest real-time signals.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static bool seen_sigrtmin, seen_sigrtmax;
+
+static void handle_signal(int sig)
+{
+if (sig == SIGRTMIN) {
+seen_sigrtmin = true;
+} else if (sig == SIGRTMAX) {
+seen_sigrtmax = true;
+} else {
+_exit(1);
+}
+}
+
+int main(void)
+{
+struct sigaction act;
+
+memset(, 0, sizeof(act));
+act.sa_handler = handle_signal;
+assert(sigaction(SIGRTMIN, , NULL) == 0);
+assert(sigaction(SIGRTMAX, , NULL) == 0);
+
+assert(kill(getpid(), SIGRTMIN) == 0);
+assert(seen_sigrtmin);
+assert(kill(getpid(), SIGRTMAX) == 0);
+assert(seen_sigrtmax);
+
+return EXIT_SUCCESS;
+}
-- 
2.43.0




[PATCH v2 1/2] linux-user: Map low priority rt signals

2024-02-12 Thread Ilya Leoshkevich
Some applications want to use low priority realtime signals (e.g.,
SIGRTMAX). Currently QEMU cannot map all target realtime signals to
host signals, and chooses to sacrifice the end of the target realtime
signal range.

Change this to the middle of that range, hoping that fewer applications
will need it.

Signed-off-by: Ilya Leoshkevich 
---
 linux-user/signal.c | 18 +-
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index d3e62ab030f..a81533b563a 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -511,13 +511,14 @@ static int core_dump_signal(int sig)
 
 static void signal_table_init(void)
 {
-int hsig, tsig, count;
+int hsig, hsig_count, tsig, tsig_count, tsig_hole, tsig_hole_size, count;
 
 /*
- * Signals are supported starting from TARGET_SIGRTMIN and going up
- * until we run out of host realtime signals.  Glibc uses the lower 2
- * RT signals and (hopefully) nobody uses the upper ones.
- * This is why SIGRTMIN (34) is generally greater than __SIGRTMIN (32).
+ * Signals are supported starting from TARGET_SIGRTMIN and up to
+ * TARGET_SIGRTMAX, potentially with a hole in the middle of this
+ * range, which, hopefully, nobody uses. Glibc uses the lower 2 RT
+ * signals; this is why SIGRTMIN (34) is generally greater than
+ * __SIGRTMIN (32).
  * To fix this properly we would need to do manual signal delivery
  * multiplexed over a single host signal.
  * Attempts for configure "missing" signals via sigaction will be
@@ -536,9 +537,16 @@ static void signal_table_init(void)
 host_to_target_signal_table[SIGABRT] = 0;
 host_to_target_signal_table[hsig++] = TARGET_SIGABRT;
 
+hsig_count = SIGRTMAX - hsig + 1;
+tsig_count = TARGET_NSIG - TARGET_SIGRTMIN + 1;
+tsig_hole_size = tsig_count - MIN(hsig_count, tsig_count);
+tsig_hole = TARGET_SIGRTMIN + (tsig_count - tsig_hole_size) / 2;
 for (tsig = TARGET_SIGRTMIN;
  hsig <= SIGRTMAX && tsig <= TARGET_NSIG;
  hsig++, tsig++) {
+if (tsig == tsig_hole) {
+tsig += tsig_hole_size;
+}
 host_to_target_signal_table[hsig] = tsig;
 }
 
-- 
2.43.0




[PATCH v2 0/2] linux-user: Map low priority rt signals

2024-02-12 Thread Ilya Leoshkevich
Hi,

There are apps out there that want to use SIGRTMAX, which linux-user
currently does not map to a host signal. The reason is that with the
current approach it's not possible to map all target signals, so it
was decided to sacrifice the end of the range.

In this series I propose to sacrifice the middle instead. Patch 1 makes
the change, patch 2 is a test.

Best regards,
Ilya

Ilya Leoshkevich (2):
  linux-user: Map low priority rt signals
  tests/tcg: Add SIGRTMIN/SIGRTMAX test

 linux-user/signal.c   | 18 +---
 tests/tcg/multiarch/linux/linux-sigrtminmax.c | 41 +++
 2 files changed, 54 insertions(+), 5 deletions(-)
 create mode 100644 tests/tcg/multiarch/linux/linux-sigrtminmax.c

-- 
2.43.0




Re: Re: [PULL 05/13] linux-user: Use walk_memory_regions for open_self_maps

2024-02-12 Thread Ilya Leoshkevich
On Mon, Feb 05, 2024 at 11:11:06AM +, Richard Purdie wrote:
> On Mon, 2024-02-05 at 13:05 +1000, Richard Henderson wrote:
> > On 1/26/24 23:52, Richard Purdie wrote:
> > > On Fri, 2024-01-26 at 16:33 +0300, Michael Tokarev wrote:
> > > > 26.01.2024 16:03, Richard Purdie wrote:
> > > > > I've run into a problem with this change.
> > > > > 
> > > > > We (Yocto Project) upgraded to qemu 8.2.0 recently and after that we
> > > > > started seeing errors cross compiling webkitgtk on x86_64 for x86_64
> > > > > during the introspection code which runs under user mode qemu.
> > > > 
> > > > Besides your observations, please be aware there's quite a few issues 
> > > > in 8.2.0.
> > > > Please take a look at 
> > > > https://gitlab.com/mjt0k/qemu/-/commits/staging-8.2/
> > > > (and https://gitlab.com/qemu-project/qemu/-/commits/staging-8.2/ which 
> > > > is updated
> > > > less often) for fixes already queued up, if you haven't looked there 
> > > > already.
> > > > 8.2.1 stable/bugfix release is scheduled for the beginning of the next 
> > > > week.
> > > 
> > > Thanks.
> > > 
> > > I should note that I did test the staging-8.2 branch and nothing there
> > > helped. The issue was also present with master as of yesterday.
> > > 
> > > https://bugzilla.yoctoproject.org/show_bug.cgi?id=15367 is Yocto
> > > Projects tracking of the issue which has the commits for master and
> > > staging-8.2 that I tested.
> > 
> > The yocto logs referenced here are not helpful for reproducing the problem.
> 
> It took me a couple of days I didn't have to workout which commit
> caused it, which versions showed the issue and how to work around it.
> 
> It looks host kernel specific since it doesn't happen on some systems
> so even with the binaries/command/environment vars, it may not be
> enough.
> 
> I was hoping the indication of the cause might help point to the fix as
> there is quite a bit of work in trying to extract this into a
> reproducer. The failure is 20 mins into a webkitgtk compile on a remote
> CI system which no longer has the context on it.
> 
> > Please extract a binary to run, inputs, and command-line.
> 
> I wish I could say that to the bug reports I get! :)
> 
> I'll do my best but finding the time is going to be a challenge.
> 
> Cheers,
> 
> Richard

I just ran into a similar crash and could reproduce it with
5005aed8a7e7 alpha-linux-user as follows:

#include 
#include 

int main(void)
{
shmat(shmget(IPC_PRIVATE, 1836016, IPC_CREAT | 0600), (void 
*)0x2804000, 0);
open("/proc/self/maps", O_RDONLY);
}

Apparently an mmap() is missing for shmat() when g>h and shmaddr is
specified. The mismatch between the host's and the guest's view of the
mapping's tail appears to be causing the SEGV.



Re: [PATCH v3 0/4] make vm-build-freebsd fixes

2024-02-07 Thread Ilya Leoshkevich
On Wed, 2024-02-07 at 15:02 +, Alex Bennée wrote:
> Ilya Leoshkevich  writes:
> 
> > v2:
> > https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg00890.html
> > v2 -> v3: Structure the meson check similar to have_asan_fiber;
> >   Reduce the context size a little (Philippe).
> > 
> > v1:
> > https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg05155.html
> > v1 -> v2: Link with libinotify instead of disabling the inotify
> >   support (Daniel).
> >   Use a bit more context lines in order to prevent the
> >   incorrect application of the test patch.
> > 
> > Hi,
> > 
> > I needed to verify that my qemu-user changes didn't break BSD, and
> > Daniel Berrange suggested vm-build-freebsd on IRC. I had several
> > problems with it, which this series resolves.
> 
> Queued to testing/next, thanks.

Hi Alex,

thanks! But I think Thomas already took it into his tree:

https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg01015.html

Best regards,
Ilya



[PATCH v3 0/4] make vm-build-freebsd fixes

2024-02-05 Thread Ilya Leoshkevich
v2: https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg00890.html
v2 -> v3: Structure the meson check similar to have_asan_fiber;
  Reduce the context size a little (Philippe).

v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg05155.html
v1 -> v2: Link with libinotify instead of disabling the inotify
  support (Daniel).
  Use a bit more context lines in order to prevent the
  incorrect application of the test patch.

Hi,

I needed to verify that my qemu-user changes didn't break BSD, and
Daniel Berrange suggested vm-build-freebsd on IRC. I had several
problems with it, which this series resolves.

Best regards,
Ilya

Ilya Leoshkevich (4):
  tests/vm: Set UseDNS=no in the sshd configuration
  tests/vm/freebsd: Reload the sshd configuration
  test-util-filemonitor: Adapt to the FreeBSD inotify rename semantics
  meson: Link with libinotify on FreeBSD

 meson.build| 23 +++
 tests/unit/test-util-filemonitor.c |  8 
 tests/vm/basevm.py |  2 ++
 tests/vm/freebsd   |  1 +
 util/meson.build   |  6 +-
 5 files changed, 35 insertions(+), 5 deletions(-)

-- 
2.43.0




[PATCH v3 2/4] tests/vm/freebsd: Reload the sshd configuration

2024-02-05 Thread Ilya Leoshkevich
After console_sshd_config(), the SSH server needs to be nudged to pick
up the new configs. The scripts for the other BSD flavors already do
this with a reboot, but a simple reload is sufficient.

Reviewed-by: Thomas Huth 
Signed-off-by: Ilya Leoshkevich 
---
 tests/vm/freebsd | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/vm/freebsd b/tests/vm/freebsd
index b581bd17fb7..1247f40a385 100755
--- a/tests/vm/freebsd
+++ b/tests/vm/freebsd
@@ -91,40 +91,41 @@ class FreeBSDVM(basevm.BaseVM):
 self.console_wait_send("Use an empty password", "\n")
 self.console_wait_send("Use a random password", "\n")
 self.console_wait("Enter password:")
 self.console_send("%s\n" % self._config["guest_pass"])
 self.console_wait("Enter password again:")
 self.console_send("%s\n" % self._config["guest_pass"])
 self.console_wait_send("Lock out",  "\n")
 self.console_wait_send("OK","yes\n")
 self.console_wait_send("Add another user",  "no\n")
 self.console_wait_send("~ #", "exit\n")
 
 # setup qemu user
 prompt = "$"
 self.console_ssh_init(prompt, self._config["guest_user"], 
self._config["guest_pass"])
 self.console_wait_send(prompt, "exit\n")
 
 # setup root user
 prompt = "root@freebsd:~ #"
 self.console_ssh_init(prompt, "root", self._config["root_pass"])
 self.console_sshd_config(prompt)
+self.console_wait_send(prompt, "service sshd reload\n")
 
 # setup virtio-blk #1 (tarfile)
 self.console_wait(prompt)
 self.console_send("echo 'chmod 666 /dev/vtbd1' >> /etc/rc.local\n")
 
 pkgs = self.get_qemu_packages_from_lcitool_json()
 self.print_step("Installing packages")
 self.ssh_root_check("pkg install -y %s\n" % " ".join(pkgs))
 
 # shutdown
 self.ssh_root(self.poweroff)
 self.wait()
 
 if os.path.exists(img):
 os.remove(img)
 os.rename(img_tmp, img)
 self.print_step("All done")
 
 if __name__ == "__main__":
 sys.exit(basevm.main(FreeBSDVM, config=FREEBSD_CONFIG))
-- 
2.43.0




[PATCH v3 4/4] meson: Link with libinotify on FreeBSD

2024-02-05 Thread Ilya Leoshkevich
make vm-build-freebsd fails with:

ld: error: undefined symbol: inotify_init1
>>> referenced by filemonitor-inotify.c:183 
(../src/util/filemonitor-inotify.c:183)
>>>   util_filemonitor-inotify.c.o:(qemu_file_monitor_new) in 
archive libqemuutil.a

On FreeBSD the inotify functions are defined in libinotify.so. Add it
to the dependencies.

Signed-off-by: Ilya Leoshkevich 
---
 meson.build  | 23 +++
 util/meson.build |  6 +-
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/meson.build b/meson.build
index b5d6dc94a83..e5d6f2d057e 100644
--- a/meson.build
+++ b/meson.build
@@ -2367,60 +2367,72 @@ if rbd.found()
 endif
 if rdma.found()
   config_host_data.set('HAVE_IBV_ADVISE_MR',
cc.has_function('ibv_advise_mr',
dependencies: rdma,
prefix: '#include 
'))
 endif
 
 have_asan_fiber = false
 if get_option('sanitizers') and \
not cc.has_function('__sanitizer_start_switch_fiber',
  args: '-fsanitize=address',
  prefix: '#include ')
   warning('Missing ASAN due to missing fiber annotation interface')
   warning('Without code annotation, the report may be inferior.')
 else
   have_asan_fiber = true
 endif
 config_host_data.set('CONFIG_ASAN_IFACE_FIBER', have_asan_fiber)
 
+have_inotify_init = cc.has_header_symbol('sys/inotify.h', 'inotify_init')
+have_inotify_init1 = cc.has_header_symbol('sys/inotify.h', 'inotify_init1')
+inotify = not_found
+if (have_inotify_init or have_inotify_init1) and host_os == 'freebsd'
+  # libinotify-kqueue
+  inotify = cc.find_library('inotify')
+  if have_inotify_init
+have_inotify_init = inotify.found()
+  endif
+  if have_inotify_init1
+have_inotify_init1 = inotify.found()
+  endif
+endif
+config_host_data.set('CONFIG_INOTIFY', have_inotify_init)
+config_host_data.set('CONFIG_INOTIFY1', have_inotify_init1)
+
 # has_header_symbol
 config_host_data.set('CONFIG_BLKZONED',
  cc.has_header_symbol('linux/blkzoned.h', 'BLKOPENZONE'))
 config_host_data.set('CONFIG_EPOLL_CREATE1',
  cc.has_header_symbol('sys/epoll.h', 'epoll_create1'))
 config_host_data.set('CONFIG_FALLOCATE_PUNCH_HOLE',
  cc.has_header_symbol('linux/falloc.h', 
'FALLOC_FL_PUNCH_HOLE') and
  cc.has_header_symbol('linux/falloc.h', 
'FALLOC_FL_KEEP_SIZE'))
 config_host_data.set('CONFIG_FALLOCATE_ZERO_RANGE',
  cc.has_header_symbol('linux/falloc.h', 
'FALLOC_FL_ZERO_RANGE'))
 config_host_data.set('CONFIG_FIEMAP',
  cc.has_header('linux/fiemap.h') and
  cc.has_header_symbol('linux/fs.h', 'FS_IOC_FIEMAP'))
 config_host_data.set('CONFIG_GETRANDOM',
  cc.has_function('getrandom') and
  cc.has_header_symbol('sys/random.h', 'GRND_NONBLOCK'))
-config_host_data.set('CONFIG_INOTIFY',
- cc.has_header_symbol('sys/inotify.h', 'inotify_init'))
-config_host_data.set('CONFIG_INOTIFY1',
- cc.has_header_symbol('sys/inotify.h', 'inotify_init1'))
 config_host_data.set('CONFIG_PRCTL_PR_SET_TIMERSLACK',
  cc.has_header_symbol('sys/prctl.h', 'PR_SET_TIMERSLACK'))
 config_host_data.set('CONFIG_RTNETLINK',
  cc.has_header_symbol('linux/rtnetlink.h', 
'IFLA_PROTO_DOWN'))
 config_host_data.set('CONFIG_SYSMACROS',
  cc.has_header_symbol('sys/sysmacros.h', 'makedev'))
 config_host_data.set('HAVE_OPTRESET',
  cc.has_header_symbol('getopt.h', 'optreset'))
 config_host_data.set('HAVE_IPPROTO_MPTCP',
  cc.has_header_symbol('netinet/in.h', 'IPPROTO_MPTCP'))
 
 # has_member
 config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID',
  cc.has_member('struct sigevent', 'sigev_notify_thread_id',
prefix: '#include '))
 config_host_data.set('HAVE_STRUCT_STAT_ST_ATIM',
  cc.has_member('struct stat', 'st_atim',
prefix: '#include '))
 config_host_data.set('HAVE_BLK_ZONE_REP_CAPACITY',
  cc.has_member('struct blk_zone', 'capacity',
@@ -4390,40 +4402,43 @@ if host_os == 'windows'
 endif
 summary_info += {'seccomp support':   seccomp}
 summary_info += {'GlusterFS support': glusterfs}
 summary_info += {'hv-balloon support': hv_balloon}
 summary_info += {'TPM support':   have_tpm}
 summary_info += {'libssh support':libssh}
 summary_info += {'lzo support':   lzo}
 summary_info += {'snappy support':snappy}
 summary_info += {'bzip2 support': libbzip2}
 summary_info += {'lzfse support': liblzfse}
 summary_info += {'zstd support':  zstd}
 summary_info += {'NUMA host support': numa}
 summary_info += {'capstone':  capstone}
 summary_info += {'libpmem support':   libp

[PATCH v3 1/4] tests/vm: Set UseDNS=no in the sshd configuration

2024-02-05 Thread Ilya Leoshkevich
make vm-build-freebsd sometimes fails with "Connection timed out during
banner exchange". The client strace shows:

13:59:30 write(3, "SSH-2.0-OpenSSH_9.3\r\n", 21) = 21
13:59:30 getpid()   = 252655
13:59:30 poll([{fd=3, events=POLLIN}], 1, 5000) = 1 ([{fd=3, 
revents=POLLIN}])
13:59:32 read(3, "S", 1)= 1
13:59:32 poll([{fd=3, events=POLLIN}], 1, 3625) = 1 ([{fd=3, 
revents=POLLIN}])
13:59:32 read(3, "S", 1)= 1
13:59:32 poll([{fd=3, events=POLLIN}], 1, 3625) = 1 ([{fd=3, 
revents=POLLIN}])
13:59:32 read(3, "H", 1)= 1

There is a 2s delay during connection, and ConnectTimeout is set to 1.
Raising it makes the issue go away, but we can do better. The server
truss shows:

888: 27.811414714 socket(PF_INET,SOCK_DGRAM|SOCK_CLOEXEC,0) = 5 (0x5)
888: 27.811765030 connect(5,{ AF_INET 10.0.2.3:53 },16) = 0 (0x0)
888: 27.812166941 sendto(5,"\^Z/\^A\0\0\^A\0\0\0\0\0\0\^A2"...,39,0,NULL,0) 
= 39 (0x27)
888: 29.363970743 poll({ 5/POLLRDNORM },1,5000) = 1 (0x1)

So the delay is due to a DNS query. Disable DNS queries in the server
config.

Reviewed-by: Thomas Huth 
Signed-off-by: Ilya Leoshkevich 
---
 tests/vm/basevm.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
index 61725b83254..c0d62c08031 100644
--- a/tests/vm/basevm.py
+++ b/tests/vm/basevm.py
@@ -406,40 +406,42 @@ def console_send(self, command):
 vm.console_socket.send(char.encode("utf-8"))
 time.sleep(0.01)
 
 def console_wait_send(self, wait, command):
 self.console_wait(wait)
 self.console_send(command)
 
 def console_ssh_init(self, prompt, user, pw):
 sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" \
  % self._config['ssh_pub_key'].rstrip()
 self.console_wait_send("login:","%s\n" % user)
 self.console_wait_send("Password:", "%s\n" % pw)
 self.console_wait_send(prompt,  "mkdir .ssh\n")
 self.console_wait_send(prompt,  sshkey_cmd)
 self.console_wait_send(prompt,  "chmod 755 .ssh\n")
 self.console_wait_send(prompt,  "chmod 644 .ssh/authorized_keys\n")
 
 def console_sshd_config(self, prompt):
 self.console_wait(prompt)
 self.console_send("echo 'PermitRootLogin yes' >> 
/etc/ssh/sshd_config\n")
+self.console_wait(prompt)
+self.console_send("echo 'UseDNS no' >> /etc/ssh/sshd_config\n")
 for var in self.envvars:
 self.console_wait(prompt)
 self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" 
% var)
 
 def print_step(self, text):
 sys.stderr.write("### %s ...\n" % text)
 
 def wait_ssh(self, wait_root=False, seconds=300, cmd="exit 0"):
 # Allow more time for VM to boot under TCG.
 if not kvm_available(self.arch):
 seconds *= self.tcg_timeout_multiplier
 starttime = datetime.datetime.now()
 endtime = starttime + datetime.timedelta(seconds=seconds)
 cmd_success = False
 while datetime.datetime.now() < endtime:
 if wait_root and self.ssh_root(cmd) == 0:
 cmd_success = True
 break
 elif self.ssh(cmd) == 0:
 cmd_success = True
-- 
2.43.0




[PATCH v3 3/4] test-util-filemonitor: Adapt to the FreeBSD inotify rename semantics

2024-02-05 Thread Ilya Leoshkevich
Unlike on Linux, on FreeBSD renaming a file when the destination
already exists results in an IN_DELETE event for that existing file:

$ FILEMONITOR_DEBUG=1 build/tests/unit/test-util-filemonitor
Rename /tmp/test-util-filemonitor-K13LI2/fish/one.txt -> 
/tmp/test-util-filemonitor-K13LI2/two.txt
Event id=2 event=2 file=one.txt
Queue event id 2 event 2 file one.txt
Queue event id 1 event 2 file two.txt
Queue event id 10002 event 2 file two.txt
Queue event id 1 event 0 file two.txt
Queue event id 10002 event 0 file two.txt
Event id=1 event=0 file=two.txt
Expected event 0 but got 2

This difference in behavior is not expected to break the real users, so
teach the test to accept it.

Suggested-by: Daniel P. Berrange 
Signed-off-by: Ilya Leoshkevich 
---
 tests/unit/test-util-filemonitor.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/tests/unit/test-util-filemonitor.c 
b/tests/unit/test-util-filemonitor.c
index a22de275955..02e67fc96ac 100644
--- a/tests/unit/test-util-filemonitor.c
+++ b/tests/unit/test-util-filemonitor.c
@@ -343,40 +343,48 @@ test_file_monitor_events(void)
   .filesrc = "fish/", .watchid =  },
 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
   .filesrc = "fish/one.txt", .watchid =  },
 { .type = QFILE_MONITOR_TEST_OP_CREATE,
   .filesrc = "fish/one.txt", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "one.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "one.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
   .filesrc = "fish/one.txt", .watchid =  },
 { .type = QFILE_MONITOR_TEST_OP_RENAME,
   .filesrc = "fish/one.txt", .filedst = "two.txt", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "one.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_DELETED },
+#ifdef __FreeBSD__
+{ .type = QFILE_MONITOR_TEST_OP_EVENT,
+  .filesrc = "two.txt", .watchid = ,
+  .eventid = QFILE_MONITOR_EVENT_DELETED },
+{ .type = QFILE_MONITOR_TEST_OP_EVENT,
+  .filesrc = "two.txt", .watchid = ,
+  .eventid = QFILE_MONITOR_EVENT_DELETED },
+#endif
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "two.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "two.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
 { .type = QFILE_MONITOR_TEST_OP_RMDIR,
   .filesrc = "fish", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_IGNORED,
   .swapnext = true },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "fish", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_DELETED },
 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
   .filesrc = "fish", .watchid =  },
 
-- 
2.43.0




[PATCH v5 0/4] target/s390x: Emulate CVDG and CVB*

2024-02-05 Thread Ilya Leoshkevich
v4: https://lists.gnu.org/archive/html/qemu-devel/2024-02/msg00434.html
v4 -> v5: Remove a redundant CVBG overflow check;
  Write the comment explaining the remaining CVBG overflow
  check;
  Add R-bs to the tests (Thomas).

v3: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06664.html
v3 -> v4: Implement CVB error handling (David/Thomas).

v2: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg05048.html
v2 -> v3: Resurrect an old CVB* patch (Thomas).
  Add Richard's R-b.

v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg02865.html
v1 -> v2: Fix !CONFIG_INT128 builds (Richard).

Hi,

Ido reported that we are missing the CVDG emulation (which is very
similar to the existing CVD emulation). This series adds it along with
a test.

Best regards,
Ilya

Ilya Leoshkevich (4):
  target/s390x: Emulate CVDG
  target/s390x: Emulate CVB, CVBY and CVBG
  tests/tcg/s390x: Test CONVERT TO DECIMAL
  tests/tcg/s390x: Test CONVERT TO BINARY

 target/s390x/helper.h|   3 +
 target/s390x/tcg/insn-data.h.inc |   5 ++
 target/s390x/tcg/int_helper.c|  97 +
 target/s390x/tcg/translate.c |  24 
 tests/tcg/s390x/Makefile.target  |   2 +
 tests/tcg/s390x/cvb.c| 102 +++
 tests/tcg/s390x/cvd.c|  63 +++
 7 files changed, 296 insertions(+)
 create mode 100644 tests/tcg/s390x/cvb.c
 create mode 100644 tests/tcg/s390x/cvd.c

-- 
2.43.0




[PATCH v5 4/4] tests/tcg/s390x: Test CONVERT TO BINARY

2024-02-05 Thread Ilya Leoshkevich
Check the CVB's, CVBY's, and CVBG's corner cases.

Co-developed-by: Pavel Zbitskiy 
Reviewed-by: Thomas Huth 
Tested-by: Thomas Huth 
Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/s390x/Makefile.target |   1 +
 tests/tcg/s390x/cvb.c   | 102 
 2 files changed, 103 insertions(+)
 create mode 100644 tests/tcg/s390x/cvb.c

diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 04e4bddd83d..e2aba2ec274 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -46,6 +46,7 @@ TESTS+=laalg
 TESTS+=add-logical-with-carry
 TESTS+=lae
 TESTS+=cvd
+TESTS+=cvb
 
 cdsg: CFLAGS+=-pthread
 cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/cvb.c b/tests/tcg/s390x/cvb.c
new file mode 100644
index 000..e1735f6b81c
--- /dev/null
+++ b/tests/tcg/s390x/cvb.c
@@ -0,0 +1,102 @@
+/*
+ * Test the CONVERT TO BINARY instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int signum;
+
+static void signal_handler(int n)
+{
+signum = n;
+}
+
+#define FAIL 0x1234567887654321
+#define OK32(x) (0x12345678 | (uint32_t)(x))
+
+static int64_t cvb(uint64_t x)
+{
+int64_t ret = FAIL;
+
+signum = -1;
+asm("cvb %[ret],%[x]" : [ret] "+r" (ret) : [x] "R" (x));
+
+return ret;
+}
+
+static int64_t cvby(uint64_t x)
+{
+int64_t ret = FAIL;
+
+signum = -1;
+asm("cvby %[ret],%[x]" : [ret] "+r" (ret) : [x] "T" (x));
+
+return ret;
+}
+
+static int64_t cvbg(__uint128_t x)
+{
+int64_t ret = FAIL;
+
+signum = -1;
+asm("cvbg %[ret],%[x]" : [ret] "+r" (ret) : [x] "T" (x));
+
+return ret;
+}
+
+int main(void)
+{
+__uint128_t m = (((__uint128_t)0x9223372036854775) << 16) | 0x8070;
+struct sigaction act;
+int err;
+
+memset(, 0, sizeof(act));
+act.sa_handler = signal_handler;
+err = sigaction(SIGFPE, , NULL);
+assert(err == 0);
+err = sigaction(SIGILL, , NULL);
+assert(err == 0);
+
+assert(cvb(0xc) == OK32(0) && signum == -1);
+assert(cvb(0x1c) == OK32(1) && signum == -1);
+assert(cvb(0x25594c) == OK32(25594) && signum == -1);
+assert(cvb(0x1d) == OK32(-1) && signum == -1);
+assert(cvb(0x2147483647c) == OK32(0x7fff) && signum == -1);
+assert(cvb(0x2147483648d) == OK32(-0x8000) && signum == -1);
+assert(cvb(0x7) == FAIL && signum == SIGILL);
+assert(cvb(0x2147483648c) == OK32(0x8000) && signum == SIGFPE);
+assert(cvb(0x30c) == OK32(0xb2d05e00) && signum == SIGFPE);
+assert(cvb(0x2147483649d) == OK32(0x7fff) && signum == SIGFPE);
+assert(cvb(0x30d) == OK32(0x4d2fa200) && signum == SIGFPE);
+
+assert(cvby(0xc) == OK32(0));
+assert(cvby(0x1c) == OK32(1));
+assert(cvby(0x25594c) == OK32(25594));
+assert(cvby(0x1d) == OK32(-1));
+assert(cvby(0x2147483647c) == OK32(0x7fff));
+assert(cvby(0x2147483648d) == OK32(-0x8000));
+assert(cvby(0x7) == FAIL && signum == SIGILL);
+assert(cvby(0x2147483648c) == OK32(0x8000) && signum == SIGFPE);
+assert(cvby(0x30c) == OK32(0xb2d05e00) && signum == SIGFPE);
+assert(cvby(0x2147483649d) == OK32(0x7fff) && signum == SIGFPE);
+assert(cvby(0x30d) == OK32(0x4d2fa200) && signum == SIGFPE);
+
+assert(cvbg(0xc) == 0);
+assert(cvbg(0x1c) == 1);
+assert(cvbg(0x25594c) == 25594);
+assert(cvbg(0x1d) == -1);
+assert(cvbg(m + 0xc) == 0x7fff);
+assert(cvbg(m + 0x1d) == -0x8000);
+assert(cvbg(0x7) == FAIL && signum == SIGILL);
+assert(cvbg(m + 0x1c) == FAIL && signum == SIGFPE);
+assert(cvbg(m + 0x2d) == FAIL && signum == SIGFPE);
+assert(cvbg(((__uint128_t)1 << 80) + 0xc) == FAIL && signum == SIGFPE);
+assert(cvbg(((__uint128_t)1 << 80) + 0xd) == FAIL && signum == SIGFPE);
+
+return EXIT_SUCCESS;
+}
-- 
2.43.0




[PATCH v5 3/4] tests/tcg/s390x: Test CONVERT TO DECIMAL

2024-02-05 Thread Ilya Leoshkevich
Check the CVD's, CVDY's, and CVDG's corner cases.

Reviewed-by: Thomas Huth 
Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/s390x/Makefile.target |  1 +
 tests/tcg/s390x/cvd.c   | 63 +
 2 files changed, 64 insertions(+)
 create mode 100644 tests/tcg/s390x/cvd.c

diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 30994dcf9c2..04e4bddd83d 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -45,6 +45,7 @@ TESTS+=clc
 TESTS+=laalg
 TESTS+=add-logical-with-carry
 TESTS+=lae
+TESTS+=cvd
 
 cdsg: CFLAGS+=-pthread
 cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/cvd.c b/tests/tcg/s390x/cvd.c
new file mode 100644
index 000..d776688985e
--- /dev/null
+++ b/tests/tcg/s390x/cvd.c
@@ -0,0 +1,63 @@
+/*
+ * Test the CONVERT TO DECIMAL instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+
+static uint64_t cvd(int32_t x)
+{
+uint64_t ret;
+
+asm("cvd %[x],%[ret]" : [ret] "=R" (ret) : [x] "r" (x));
+
+return ret;
+}
+
+static uint64_t cvdy(int32_t x)
+{
+uint64_t ret;
+
+asm("cvdy %[x],%[ret]" : [ret] "=T" (ret) : [x] "r" (x));
+
+return ret;
+}
+
+static __uint128_t cvdg(int64_t x)
+{
+__uint128_t ret;
+
+asm("cvdg %[x],%[ret]" : [ret] "=T" (ret) : [x] "r" (x));
+
+return ret;
+}
+
+int main(void)
+{
+__uint128_t m = (((__uint128_t)0x9223372036854775) << 16) | 0x8070;
+
+assert(cvd(0) == 0xc);
+assert(cvd(1) == 0x1c);
+assert(cvd(25594) == 0x25594c);
+assert(cvd(-1) == 0x1d);
+assert(cvd(0x7fff) == 0x2147483647c);
+assert(cvd(-0x8000) == 0x2147483648d);
+
+assert(cvdy(0) == 0xc);
+assert(cvdy(1) == 0x1c);
+assert(cvdy(25594) == 0x25594c);
+assert(cvdy(-1) == 0x1d);
+assert(cvdy(0x7fff) == 0x2147483647c);
+assert(cvdy(-0x8000) == 0x2147483648d);
+
+assert(cvdg(0) == 0xc);
+assert(cvdg(1) == 0x1c);
+assert(cvdg(25594) == 0x25594c);
+assert(cvdg(-1) == 0x1d);
+assert(cvdg(0x7fff) == (m + 0xc));
+assert(cvdg(-0x8000) == (m + 0x1d));
+
+return EXIT_SUCCESS;
+}
-- 
2.43.0




[PATCH v5 1/4] target/s390x: Emulate CVDG

2024-02-05 Thread Ilya Leoshkevich
CVDG is the same as CVD, except that it converts 64 bits into 128,
rather than 32 into 64. Create a new helper, which uses Int128
wrappers.

Reported-by: Ido Plat 
Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 target/s390x/helper.h|  1 +
 target/s390x/tcg/insn-data.h.inc |  1 +
 target/s390x/tcg/int_helper.c| 21 +
 target/s390x/tcg/translate.c |  8 
 4 files changed, 31 insertions(+)

diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 05102578fc9..332a9a9c632 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -89,6 +89,7 @@ DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqxb, TCG_CALL_NO_WG, i128, env, i128)
 DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
+DEF_HELPER_FLAGS_1(cvdg, TCG_CALL_NO_RWG_SE, i128, s64)
 DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_WG, void, env, i32, i64, i64)
 DEF_HELPER_FLAGS_4(pka, TCG_CALL_NO_WG, void, env, i64, i64, i32)
 DEF_HELPER_FLAGS_4(pku, TCG_CALL_NO_WG, void, env, i64, i64, i32)
diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc
index 2f07f39d9cb..388dcb8dbbc 100644
--- a/target/s390x/tcg/insn-data.h.inc
+++ b/target/s390x/tcg/insn-data.h.inc
@@ -296,6 +296,7 @@
 /* CONVERT TO DECIMAL */
 C(0x4e00, CVD, RX_a,  Z,   r1_o, a2, 0, 0, cvd, 0)
 C(0xe326, CVDY,RXY_a, LD,  r1_o, a2, 0, 0, cvd, 0)
+C(0xe32e, CVDG,RXY_a, Z,   r1_o, a2, 0, 0, cvdg, 0)
 /* CONVERT TO FIXED */
 F(0xb398, CFEBR,   RRF_e, Z,   0, e2, new, r1_32, cfeb, 0, IF_BFP)
 F(0xb399, CFDBR,   RRF_e, Z,   0, f2, new, r1_32, cfdb, 0, IF_BFP)
diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c
index eb8e6dd1b57..121e3006a65 100644
--- a/target/s390x/tcg/int_helper.c
+++ b/target/s390x/tcg/int_helper.c
@@ -118,6 +118,27 @@ uint64_t HELPER(cvd)(int32_t reg)
 return dec;
 }
 
+Int128 HELPER(cvdg)(int64_t reg)
+{
+/* positive 0 */
+Int128 dec = int128_make64(0x0c);
+Int128 bin = int128_makes64(reg);
+Int128 base = int128_make64(10);
+int shift;
+
+if (!int128_nonneg(bin)) {
+bin = int128_neg(bin);
+dec = int128_make64(0x0d);
+}
+
+for (shift = 4; (shift < 128) && int128_nz(bin); shift += 4) {
+dec = int128_or(dec, int128_lshift(int128_remu(bin, base), shift));
+bin = int128_divu(bin, base);
+}
+
+return dec;
+}
+
 uint64_t HELPER(popcnt)(uint64_t val)
 {
 /* Note that we don't fold past bytes. */
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index a5fd9cccaa5..c2fdc920a50 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -2233,6 +2233,14 @@ static DisasJumpType op_cvd(DisasContext *s, DisasOps *o)
 return DISAS_NEXT;
 }
 
+static DisasJumpType op_cvdg(DisasContext *s, DisasOps *o)
+{
+TCGv_i128 t = tcg_temp_new_i128();
+gen_helper_cvdg(t, o->in1);
+tcg_gen_qemu_st_i128(t, o->in2, get_mem_index(s), MO_TE | MO_128);
+return DISAS_NEXT;
+}
+
 static DisasJumpType op_ct(DisasContext *s, DisasOps *o)
 {
 int m3 = get_field(s, m3);
-- 
2.43.0




[PATCH v5 2/4] target/s390x: Emulate CVB, CVBY and CVBG

2024-02-05 Thread Ilya Leoshkevich
Convert to Binary - counterparts of the already implemented Convert
to Decimal (CVD*) instructions.
Example from the Principles of Operation: 25594C becomes 63FA.

Co-developed-by: Pavel Zbitskiy 
Signed-off-by: Ilya Leoshkevich 
---
 target/s390x/helper.h|  2 +
 target/s390x/tcg/insn-data.h.inc |  4 ++
 target/s390x/tcg/int_helper.c| 76 
 target/s390x/tcg/translate.c | 16 +++
 4 files changed, 98 insertions(+)

diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 332a9a9c632..cc1c20e9e3f 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -88,6 +88,8 @@ DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_NO_RWG_SE, i32, env, i128, 
i64)
 DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqxb, TCG_CALL_NO_WG, i128, env, i128)
+DEF_HELPER_3(cvb, void, env, i32, i64)
+DEF_HELPER_FLAGS_2(cvbg, TCG_CALL_NO_WG, i64, env, i128)
 DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
 DEF_HELPER_FLAGS_1(cvdg, TCG_CALL_NO_RWG_SE, i128, s64)
 DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_WG, void, env, i32, i64, i64)
diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc
index 388dcb8dbbc..e7d61cdec28 100644
--- a/target/s390x/tcg/insn-data.h.inc
+++ b/target/s390x/tcg/insn-data.h.inc
@@ -293,6 +293,10 @@
 D(0xec73, CLFIT,   RIE_a, GIE, r1_32u, i2_16u, 0, 0, ct, 0, 1)
 D(0xec71, CLGIT,   RIE_a, GIE, r1_o, i2_16u, 0, 0, ct, 0, 1)
 
+/* CONVERT TO BINARY */
+C(0x4f00, CVB, RX_a,  Z,   la2, 0, 0, 0, cvb, 0)
+C(0xe306, CVBY,RXY_a, LD,  la2, 0, 0, 0, cvb, 0)
+C(0xe30e, CVBG,RXY_a, Z,   la2, 0, r1, 0, cvbg, 0)
 /* CONVERT TO DECIMAL */
 C(0x4e00, CVD, RX_a,  Z,   r1_o, a2, 0, 0, cvd, 0)
 C(0xe326, CVDY,RXY_a, LD,  r1_o, a2, 0, 0, cvd, 0)
diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c
index 121e3006a65..2af970f2c8b 100644
--- a/target/s390x/tcg/int_helper.c
+++ b/target/s390x/tcg/int_helper.c
@@ -25,6 +25,7 @@
 #include "exec/exec-all.h"
 #include "qemu/host-utils.h"
 #include "exec/helper-proto.h"
+#include "exec/cpu_ldst.h"
 
 /* #define DEBUG_HELPER */
 #ifdef DEBUG_HELPER
@@ -98,6 +99,81 @@ Int128 HELPER(divu64)(CPUS390XState *env, uint64_t ah, 
uint64_t al, uint64_t b)
 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
 }
 
+void HELPER(cvb)(CPUS390XState *env, uint32_t r1, uint64_t dec)
+{
+int64_t pow10 = 1, bin = 0;
+int digit, sign;
+
+sign = dec & 0xf;
+if (sign < 0xa) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec >>= 4;
+
+while (dec) {
+digit = dec & 0xf;
+if (digit > 0x9) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec >>= 4;
+bin += digit * pow10;
+pow10 *= 10;
+}
+
+if (sign == 0xb || sign == 0xd) {
+bin = -bin;
+}
+
+/* R1 is updated even on fixed-point-divide exception. */
+env->regs[r1] = (env->regs[r1] & 0xULL) | (uint32_t)bin;
+if (bin != (int32_t)bin) {
+tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
+}
+}
+
+uint64_t HELPER(cvbg)(CPUS390XState *env, Int128 dec)
+{
+uint64_t dec64[] = {int128_getlo(dec), int128_gethi(dec)};
+int64_t bin = 0, pow10, tmp;
+int digit, i, sign;
+
+sign = dec64[0] & 0xf;
+if (sign < 0xa) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec64[0] >>= 4;
+pow10 = (sign == 0xb || sign == 0xd) ? -1 : 1;
+
+for (i = 1; i < 20; i++) {
+digit = dec64[i >> 4] & 0xf;
+if (digit > 0x9) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec64[i >> 4] >>= 4;
+/*
+ * Prepend the next digit and check for overflow. The multiplication
+ * cannot overflow, since, conveniently, the int64_t limits are
+ * approximately +-9.2E+18. If bin is zero, the addition cannot
+ * overflow. Otherwise bin is known to have the same sign as the rhs
+ * addend, in which case overflow happens if and only if the result
+ * has a different sign.
+ */
+tmp = bin + pow10 * digit;
+if (bin && ((tmp ^ bin) < 0)) {
+tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
+}
+bin = tmp;
+pow10 *= 10;
+}
+
+g_assert(!dec64[0]);
+if (dec64[1]) {
+tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
+}
+
+return bin;
+}
+
 uint64_t HELPER(cvd)(int32_t reg)
 {
 /* positive 0 */
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index c2fdc920a50..325b25959d3 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -2223,6 +2223,22 @@ static DisasJumpType op_csp(DisasContex

Re: Re: [PATCH v2 4/4] meson: Link with libinotify on FreeBSD

2024-02-05 Thread Ilya Leoshkevich
On Mon, Feb 05, 2024 at 07:36:32PM +0100, Philippe Mathieu-Daudé wrote:
> Hi Ilya,
> 
> On 5/2/24 19:11, Ilya Leoshkevich wrote:
> > make vm-build-freebsd fails with:
> > 
> >  ld: error: undefined symbol: inotify_init1
> >  >>> referenced by filemonitor-inotify.c:183 
> > (../src/util/filemonitor-inotify.c:183)
> >  >>>   util_filemonitor-inotify.c.o:(qemu_file_monitor_new) 
> > in archive libqemuutil.a
> > 
> > On FreeBSD inotify functions are defined in libinotify.so. Add it to
> > the dependencies.
> > 
> > Signed-off-by: Ilya Leoshkevich 
> > ---
> >   meson.build  | 12 +++-
> >   util/meson.build |  6 +-
> >   2 files changed, 16 insertions(+), 2 deletions(-)
> 
> (for some reason your git-diff context is very verbose,
>  making review somehow annoying).

This is because of patch 3. It's essentially the snippet that Daniel
posted, except that patch -p1 applied it at a wrong location! So I
figured I'll send this series with a larger context, but I couldn't
find how to apply this setting to just 1 patch in git send-email.

> > +# libinotify-kqueue
> > +inotify = not_found
> > +if host_os == 'freebsd'
> > +  inotify = cc.find_library('inotify')
> > +endif
> > +
> >   #
> >   # config-host.h #
> >   #
> 
> 
> > @@ -2376,61 +2382,62 @@ have_asan_fiber = false
> >   if get_option('sanitizers') and \
> >  not cc.has_function('__sanitizer_start_switch_fiber',
> >args: '-fsanitize=address',
> >prefix: '#include ')
> > warning('Missing ASAN due to missing fiber annotation interface')
> > warning('Without code annotation, the report may be inferior.')
> >   else
> > have_asan_fiber = true
> >   endif
> >   config_host_data.set('CONFIG_ASAN_IFACE_FIBER', have_asan_fiber)
> >   # has_header_symbol
> 
> 
> >   config_host_data.set('CONFIG_INOTIFY',
> >cc.has_header_symbol('sys/inotify.h', 
> > 'inotify_init'))
> >   config_host_data.set('CONFIG_INOTIFY1',
> > - cc.has_header_symbol('sys/inotify.h', 
> > 'inotify_init1'))
> > + cc.has_header_symbol('sys/inotify.h', 
> > 'inotify_init1') and
> > + (host_os != 'freebsd' or inotify.found()))
> 
> Maybe we could use the same pattern as 'have_asan_fiber':
> 
>  have_inotify_init1 = cc.has_header_symbol('sys/inotify.h', 'inotify_init1')
>  if have_inotify_init1 and host_os == 'freebsd'
>have_inotify_init1 = cc.find_library('inotify')
>  endif
>  config_host_data.set('CONFIG_INOTIFY1', have_inotify_init1)

I agree, this looks nicer. I will send a v3.

> I wonder why we don't need the similar library check for the
> inotify_init symbol.

Sounds reasonable, it's just that currently the respective config value
is used only in linux-user, but for completeness it won't hurt.

> 
> Regards,
> 
> Phil.



Re: Re: [PATCH v4 2/4] target/s390x: Emulate CVB, CVBY and CVBG

2024-02-05 Thread Ilya Leoshkevich
On Mon, Feb 05, 2024 at 06:04:27PM +0100, Thomas Huth wrote:
> On 02/02/2024 15.11, Ilya Leoshkevich wrote:
> > Convert to Binary - counterparts of the already implemented Convert
> > to Decimal (CVD*) instructions.
> > Example from the Principles of Operation: 25594C becomes 63FA.
> > 
> > Co-developed-by: Pavel Zbitskiy 
> > Signed-off-by: Ilya Leoshkevich 
> > ---
> >   target/s390x/helper.h|  2 +
> >   target/s390x/tcg/insn-data.h.inc |  4 ++
> >   target/s390x/tcg/int_helper.c| 72 
> >   target/s390x/tcg/translate.c | 16 +++
> >   4 files changed, 94 insertions(+)

[...]

> > +uint64_t HELPER(cvbg)(CPUS390XState *env, Int128 dec)
> > +{
> > +uint64_t dec64[] = {int128_getlo(dec), int128_gethi(dec)};
> > +int64_t bin = 0, pow10, tmp;
> > +int digit, i, sign;
> > +
> > +sign = dec64[0] & 0xf;
> > +if (sign < 0xa) {
> > +tcg_s390_data_exception(env, 0, GETPC());
> > +}
> > +dec64[0] >>= 4;
> > +pow10 = (sign == 0xb || sign == 0xd) ? -1 : 1;
> > +
> > +for (i = 1; i < 20; i++) {
> > +digit = dec64[i >> 4] & 0xf;
> > +if (digit > 0x9) {
> > +tcg_s390_data_exception(env, 0, GETPC());
> > +}
> > +dec64[i >> 4] >>= 4;
> > +tmp = pow10 * digit;
> > +if (digit && ((tmp ^ pow10) < 0)) {
> 
> That tmp ^ pow10 caused some frowning for me first, but it's just a check
> whether the sign changed, right? ... a comment in front of the line might be
> helpful.

Good point about writing a comment, I tried to elaborate why checking
the sign is justified, and realized that it's actually redundant.
The int64_t bounds are roughly +-9.2E+18, and the last pow10 value in
this loop is +-1E+18. The multiplication cannot overflow.

> > +tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
> > +}
> > +tmp = bin + tmp;
> > +if (bin && ((tmp ^ bin) < 0)) {

The addition, however, can, e.g., bin=9E+17 and tmp=9E+18.
So I'll send a v5 without the first check and with a comment.

[...]



[PATCH v2 3/4] tests/test-util-filemonitor: Adapt to FreeBSD inotify rename semantics

2024-02-05 Thread Ilya Leoshkevich
Unlike on Linux, on FreeBSD renaming a file when the destination
already exists results in IN_DELETE event for that existing file:

$ FILEMONITOR_DEBUG=1 build/tests/unit/test-util-filemonitor
Rename /tmp/test-util-filemonitor-K13LI2/fish/one.txt -> 
/tmp/test-util-filemonitor-K13LI2/two.txt
Event id=2 event=2 file=one.txt
Queue event id 2 event 2 file one.txt
Queue event id 1 event 2 file two.txt
Queue event id 10002 event 2 file two.txt
Queue event id 1 event 0 file two.txt
Queue event id 10002 event 0 file two.txt
Event id=1 event=0 file=two.txt
Expected event 0 but got 2

This difference in behavior is not expected to break the real users, so
teach the test to accept it.

Suggested-by: Daniel P. Berrange 
Signed-off-by: Ilya Leoshkevich 
---
 tests/unit/test-util-filemonitor.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/tests/unit/test-util-filemonitor.c 
b/tests/unit/test-util-filemonitor.c
index a22de275955..02e67fc96ac 100644
--- a/tests/unit/test-util-filemonitor.c
+++ b/tests/unit/test-util-filemonitor.c
@@ -333,60 +333,68 @@ test_file_monitor_events(void)
 
 
 { .type = QFILE_MONITOR_TEST_OP_MKDIR,
   .filesrc = "fish", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "fish", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
   .filesrc = "fish/", .watchid =  },
 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
   .filesrc = "fish/one.txt", .watchid =  },
 { .type = QFILE_MONITOR_TEST_OP_CREATE,
   .filesrc = "fish/one.txt", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "one.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "one.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
   .filesrc = "fish/one.txt", .watchid =  },
 { .type = QFILE_MONITOR_TEST_OP_RENAME,
   .filesrc = "fish/one.txt", .filedst = "two.txt", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "one.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_DELETED },
+#ifdef __FreeBSD__
+{ .type = QFILE_MONITOR_TEST_OP_EVENT,
+  .filesrc = "two.txt", .watchid = ,
+  .eventid = QFILE_MONITOR_EVENT_DELETED },
+{ .type = QFILE_MONITOR_TEST_OP_EVENT,
+  .filesrc = "two.txt", .watchid = ,
+  .eventid = QFILE_MONITOR_EVENT_DELETED },
+#endif
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "two.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "two.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
 { .type = QFILE_MONITOR_TEST_OP_RMDIR,
   .filesrc = "fish", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_IGNORED,
   .swapnext = true },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "fish", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_DELETED },
 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
   .filesrc = "fish", .watchid =  },
 
 
 { .type = QFILE_MONITOR_TEST_OP_UNLINK,
   .filesrc = "two.txt", },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "two.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_DELETED },
 { .type = QFILE_MONITOR_TEST_OP_EVENT,
   .filesrc = "two.txt", .watchid = ,
   .eventid = QFILE_MONITOR_EVENT_DELETED },
 
-- 
2.43.0




[PATCH v2 1/4] tests/vm: Set UseDNS=no in the sshd configuration

2024-02-05 Thread Ilya Leoshkevich
make vm-build-freebsd sometimes fails with "Connection timed out during
banner exchange". The client strace shows:

13:59:30 write(3, "SSH-2.0-OpenSSH_9.3\r\n", 21) = 21
13:59:30 getpid()   = 252655
13:59:30 poll([{fd=3, events=POLLIN}], 1, 5000) = 1 ([{fd=3, 
revents=POLLIN}])
13:59:32 read(3, "S", 1)= 1
13:59:32 poll([{fd=3, events=POLLIN}], 1, 3625) = 1 ([{fd=3, 
revents=POLLIN}])
13:59:32 read(3, "S", 1)= 1
13:59:32 poll([{fd=3, events=POLLIN}], 1, 3625) = 1 ([{fd=3, 
revents=POLLIN}])
13:59:32 read(3, "H", 1)= 1

There is a 2s delay during connection, and ConnectTimeout is set to 1.
Raising it makes the issue go away, but we can do better. The server
truss shows:

888: 27.811414714 socket(PF_INET,SOCK_DGRAM|SOCK_CLOEXEC,0) = 5 (0x5)
888: 27.811765030 connect(5,{ AF_INET 10.0.2.3:53 },16) = 0 (0x0)
888: 27.812166941 sendto(5,"\^Z/\^A\0\0\^A\0\0\0\0\0\0\^A2"...,39,0,NULL,0) 
= 39 (0x27)
888: 29.363970743 poll({ 5/POLLRDNORM },1,5000) = 1 (0x1)

So the delay is due to a DNS query. Disable DNS queries in the server
config.

Reviewed-by: Thomas Huth 
Signed-off-by: Ilya Leoshkevich 
---
 tests/vm/basevm.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
index 61725b83254..c0d62c08031 100644
--- a/tests/vm/basevm.py
+++ b/tests/vm/basevm.py
@@ -396,60 +396,62 @@ def console_consume(self):
 self.console_log(output)
 vm.console_socket.setblocking(1)
 
 def console_send(self, command):
 vm = self._guest
 if self.debug:
 logline = re.sub("\n", "", command)
 logline = re.sub("[\x00-\x1f]", ".", logline)
 sys.stderr.write("con send: %s\n" % logline)
 for char in list(command):
 vm.console_socket.send(char.encode("utf-8"))
 time.sleep(0.01)
 
 def console_wait_send(self, wait, command):
 self.console_wait(wait)
 self.console_send(command)
 
 def console_ssh_init(self, prompt, user, pw):
 sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" \
  % self._config['ssh_pub_key'].rstrip()
 self.console_wait_send("login:","%s\n" % user)
 self.console_wait_send("Password:", "%s\n" % pw)
 self.console_wait_send(prompt,  "mkdir .ssh\n")
 self.console_wait_send(prompt,  sshkey_cmd)
 self.console_wait_send(prompt,  "chmod 755 .ssh\n")
 self.console_wait_send(prompt,  "chmod 644 .ssh/authorized_keys\n")
 
 def console_sshd_config(self, prompt):
 self.console_wait(prompt)
 self.console_send("echo 'PermitRootLogin yes' >> 
/etc/ssh/sshd_config\n")
+self.console_wait(prompt)
+self.console_send("echo 'UseDNS no' >> /etc/ssh/sshd_config\n")
 for var in self.envvars:
 self.console_wait(prompt)
 self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" 
% var)
 
 def print_step(self, text):
 sys.stderr.write("### %s ...\n" % text)
 
 def wait_ssh(self, wait_root=False, seconds=300, cmd="exit 0"):
 # Allow more time for VM to boot under TCG.
 if not kvm_available(self.arch):
 seconds *= self.tcg_timeout_multiplier
 starttime = datetime.datetime.now()
 endtime = starttime + datetime.timedelta(seconds=seconds)
 cmd_success = False
 while datetime.datetime.now() < endtime:
 if wait_root and self.ssh_root(cmd) == 0:
 cmd_success = True
 break
 elif self.ssh(cmd) == 0:
 cmd_success = True
 break
 seconds = (endtime - datetime.datetime.now()).total_seconds()
 logging.debug("%ds before timeout", seconds)
 time.sleep(1)
 if not cmd_success:
 raise Exception("Timeout while waiting for guest ssh")
 
 def shutdown(self):
 self._guest.shutdown(timeout=self._shutdown_timeout)
 
-- 
2.43.0




[PATCH v2 2/4] tests/vm/freebsd: Reload the sshd configuration

2024-02-05 Thread Ilya Leoshkevich
After console_sshd_config(), the SSH server needs to be nudged to pick
up the new configs. The scripts for the other BSD flavors already do
this with a reboot, but a simple reload is sufficient.

Signed-off-by: Ilya Leoshkevich 
---
 tests/vm/freebsd | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/vm/freebsd b/tests/vm/freebsd
index b581bd17fb7..1247f40a385 100755
--- a/tests/vm/freebsd
+++ b/tests/vm/freebsd
@@ -81,50 +81,51 @@ class FreeBSDVM(basevm.BaseVM):
 self.console_wait("Full name")
 self.console_send("%s\n" % self._config["guest_user"])
 self.console_wait_send("Uid",   "\n")
 self.console_wait_send("Login group",   "\n")
 self.console_wait_send("Login group",   "\n")
 self.console_wait_send("Login class",   "\n")
 self.console_wait_send("Shell", "\n")
 self.console_wait_send("Home directory","\n")
 self.console_wait_send("Home directory perm",   "\n")
 self.console_wait_send("Use password",  "\n")
 self.console_wait_send("Use an empty password", "\n")
 self.console_wait_send("Use a random password", "\n")
 self.console_wait("Enter password:")
 self.console_send("%s\n" % self._config["guest_pass"])
 self.console_wait("Enter password again:")
 self.console_send("%s\n" % self._config["guest_pass"])
 self.console_wait_send("Lock out",  "\n")
 self.console_wait_send("OK","yes\n")
 self.console_wait_send("Add another user",  "no\n")
 self.console_wait_send("~ #", "exit\n")
 
 # setup qemu user
 prompt = "$"
 self.console_ssh_init(prompt, self._config["guest_user"], 
self._config["guest_pass"])
 self.console_wait_send(prompt, "exit\n")
 
 # setup root user
 prompt = "root@freebsd:~ #"
 self.console_ssh_init(prompt, "root", self._config["root_pass"])
 self.console_sshd_config(prompt)
+self.console_wait_send(prompt, "service sshd reload\n")
 
 # setup virtio-blk #1 (tarfile)
 self.console_wait(prompt)
 self.console_send("echo 'chmod 666 /dev/vtbd1' >> /etc/rc.local\n")
 
 pkgs = self.get_qemu_packages_from_lcitool_json()
 self.print_step("Installing packages")
 self.ssh_root_check("pkg install -y %s\n" % " ".join(pkgs))
 
 # shutdown
 self.ssh_root(self.poweroff)
 self.wait()
 
 if os.path.exists(img):
 os.remove(img)
 os.rename(img_tmp, img)
 self.print_step("All done")
 
 if __name__ == "__main__":
 sys.exit(basevm.main(FreeBSDVM, config=FREEBSD_CONFIG))
-- 
2.43.0




[PATCH v2 4/4] meson: Link with libinotify on FreeBSD

2024-02-05 Thread Ilya Leoshkevich
make vm-build-freebsd fails with:

ld: error: undefined symbol: inotify_init1
>>> referenced by filemonitor-inotify.c:183 
(../src/util/filemonitor-inotify.c:183)
>>>   util_filemonitor-inotify.c.o:(qemu_file_monitor_new) in 
archive libqemuutil.a

On FreeBSD inotify functions are defined in libinotify.so. Add it to
the dependencies.

Signed-off-by: Ilya Leoshkevich 
---
 meson.build  | 12 +++-
 util/meson.build |  6 +-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/meson.build b/meson.build
index b5d6dc94a83..819cdedebe2 100644
--- a/meson.build
+++ b/meson.build
@@ -1982,60 +1982,66 @@ if libbpf.found() and not cc.links('''
#include 
int main(void)
{
  bpf_object__destroy_skeleton(NULL);
  return 0;
}''', dependencies: libbpf)
   libbpf = not_found
   if get_option('bpf').enabled()
 error('libbpf skeleton test failed')
   else
 warning('libbpf skeleton test failed, disabling')
   endif
 endif
 
 # libxdp
 libxdp = not_found
 if not get_option('af_xdp').auto() or have_system
 libxdp = dependency('libxdp', required: get_option('af_xdp'),
 version: '>=1.4.0', method: 'pkg-config')
 endif
 
 # libdw
 libdw = not_found
 if not get_option('libdw').auto() or \
 (not get_option('prefer_static') and (have_system or have_user))
 libdw = dependency('libdw',
method: 'pkg-config',
required: get_option('libdw'))
 endif
 
+# libinotify-kqueue
+inotify = not_found
+if host_os == 'freebsd'
+  inotify = cc.find_library('inotify')
+endif
+
 #
 # config-host.h #
 #
 
 config_host_data = configuration_data()
 
 audio_drivers_selected = []
 if have_system
   audio_drivers_available = {
 'alsa': alsa.found(),
 'coreaudio': coreaudio.found(),
 'dsound': dsound.found(),
 'jack': jack.found(),
 'oss': oss.found(),
 'pa': pulse.found(),
 'pipewire': pipewire.found(),
 'sdl': sdl.found(),
 'sndio': sndio.found(),
   }
   foreach k, v: audio_drivers_available
 config_host_data.set('CONFIG_AUDIO_' + k.to_upper(), v)
   endforeach
 
   # Default to native drivers first, OSS second, SDL third
   audio_drivers_priority = \
 [ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss' ] + \
 (host_os == 'linux' ? [] : [ 'sdl' ])
   audio_drivers_default = []
   foreach k: audio_drivers_priority
 if audio_drivers_available[k]
@@ -2376,61 +2382,62 @@ have_asan_fiber = false
 if get_option('sanitizers') and \
not cc.has_function('__sanitizer_start_switch_fiber',
  args: '-fsanitize=address',
  prefix: '#include ')
   warning('Missing ASAN due to missing fiber annotation interface')
   warning('Without code annotation, the report may be inferior.')
 else
   have_asan_fiber = true
 endif
 config_host_data.set('CONFIG_ASAN_IFACE_FIBER', have_asan_fiber)
 
 # has_header_symbol
 config_host_data.set('CONFIG_BLKZONED',
  cc.has_header_symbol('linux/blkzoned.h', 'BLKOPENZONE'))
 config_host_data.set('CONFIG_EPOLL_CREATE1',
  cc.has_header_symbol('sys/epoll.h', 'epoll_create1'))
 config_host_data.set('CONFIG_FALLOCATE_PUNCH_HOLE',
  cc.has_header_symbol('linux/falloc.h', 
'FALLOC_FL_PUNCH_HOLE') and
  cc.has_header_symbol('linux/falloc.h', 
'FALLOC_FL_KEEP_SIZE'))
 config_host_data.set('CONFIG_FALLOCATE_ZERO_RANGE',
  cc.has_header_symbol('linux/falloc.h', 
'FALLOC_FL_ZERO_RANGE'))
 config_host_data.set('CONFIG_FIEMAP',
  cc.has_header('linux/fiemap.h') and
  cc.has_header_symbol('linux/fs.h', 'FS_IOC_FIEMAP'))
 config_host_data.set('CONFIG_GETRANDOM',
  cc.has_function('getrandom') and
  cc.has_header_symbol('sys/random.h', 'GRND_NONBLOCK'))
 config_host_data.set('CONFIG_INOTIFY',
  cc.has_header_symbol('sys/inotify.h', 'inotify_init'))
 config_host_data.set('CONFIG_INOTIFY1',
- cc.has_header_symbol('sys/inotify.h', 'inotify_init1'))
+ cc.has_header_symbol('sys/inotify.h', 'inotify_init1') and
+ (host_os != 'freebsd' or inotify.found()))
 config_host_data.set('CONFIG_PRCTL_PR_SET_TIMERSLACK',
  cc.has_header_symbol('sys/prctl.h', 'PR_SET_TIMERSLACK'))
 config_host_data.set('CONFIG_RTNETLINK',
  cc.has_header_symbol('linux/rtnetlink.h', 
'IFLA_PROTO_DOWN'))
 config_host_data.set('CONFIG_SYSMACROS',
  cc.has_header_symbol('sys/sysmacros.h', 'makedev'))
 config_host_data.set('HAVE_OPTRESET',
  cc.has_header_symbol('getopt.h', 'optreset'))
 config_host_data.set('HAVE_IPPROTO_MPTCP',
  cc.has_header_symbol('netinet/in.h', 'IPPROTO_MPTCP'))
 
 # has_member
 config_host_data.set('HAVE_S

[PATCH v2 0/4] make vm-build-freebsd fixes

2024-02-05 Thread Ilya Leoshkevich
v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg05155.html
v1 -> v2: Link with libinotify instead of disabling the inotify
  support (Daniel).
  Use a bit more context lines in order to prevent the
  incorrect application of the test patch.

Hi,

I needed to verify that my qemu-user changes didn't break BSD, and
Daniel Berrange suggested vm-build-freebsd on IRC. I had several
problems with it, which this series resolves.

Best regards,
Ilya

Ilya Leoshkevich (4):
  tests/vm: Set UseDNS=no in the sshd configuration
  tests/vm/freebsd: Reload the sshd configuration
  tests/test-util-filemonitor: Adapt to FreeBSD inotify rename semantics
  meson: Link with libinotify on FreeBSD

 meson.build| 12 +++-
 tests/unit/test-util-filemonitor.c |  8 
 tests/vm/basevm.py |  2 ++
 tests/vm/freebsd   |  1 +
 util/meson.build   |  6 +-
 5 files changed, 27 insertions(+), 2 deletions(-)

-- 
2.43.0




Re: [PATCH 3/3] meson: Disable CONFIG_NOTIFY1 on FreeBSD

2024-02-05 Thread Ilya Leoshkevich
On Mon, 2024-02-05 at 15:31 +, Daniel P. Berrangé wrote:
> On Mon, Feb 05, 2024 at 08:23:41AM -0700, Warner Losh wrote:
> > On Wed, Jan 31, 2024 at 9:42 AM Daniel P. Berrangé
> > 
> > wrote:
> > 
> > > On Wed, Jan 31, 2024 at 05:24:10PM +0100, Philippe Mathieu-Daudé
> > > wrote:
> > > > Hi,
> > > > 
> > > > Warner, do you remember what this is about?
> > > > 
> > > > (
> > > https://cgit.freebsd.org/ports/commit/emulators/qemu-devel/files/patch-util_meson.build?id=2ab482e2c8f51eae7ffd747685b7f181fe1b3809
> > > > isn't very verbose).
> > > 
> > > That's simply going to workaround our incomplete feature
> > > check. We look for sys/inotify.h and if present, we
> > > assume that is in the C library. That's true on Linux,
> > > but not true on *BSD, hence the undefined symbol.
> > > 
> > > We need to augment the header file check with a linker
> > > symbol check for the C library.
> > > 
> > > If we wanted to also check for -linotify that'd make
> > > it portable to BSD, but not the behaviour difference
> > > mentioned below.
> > > 
> > > > 
> > > > On 25/1/24 20:48, Ilya Leoshkevich wrote:
> > > > > make vm-build-freebsd fails with:
> > > > > 
> > > > >  ld: error: undefined symbol: inotify_init1
> > > > >  >>> referenced by filemonitor-inotify.c:183
> > > (../src/util/filemonitor-inotify.c:183)
> > > > >  >>>
> > >  util_filemonitor-inotify.c.o:(qemu_file_monitor_new) in archive
> > > libqemuutil.a
> > > > > 
> > > > > On FreeBSD inotify functions are defined in libinotify.so, so
> > > > > it might
> > > > > be tempting to add it to the dependencies. Doing so, however,
> > > > > reveals
> > > > > that this library handles rename events differently from
> > > > > Linux:
> > > > > 
> > > > >  $ FILEMONITOR_DEBUG=1 build/tests/unit/test-util-
> > > > > filemonitor
> > > > >  Rename /tmp/test-util-filemonitor-K13LI2/fish/one.txt ->
> > > /tmp/test-util-filemonitor-K13LI2/two.txt
> > > > >  Event id=2 event=2 file=one.txt
> > > > >  Queue event id 2 event 2 file one.txt
> > > > >  Queue event id 1 event 2 file two.txt
> > > > >  Queue event id 10002 event 2 file two.txt
> > > > >  Queue event id 1 event 0 file two.txt
> > > > >  Queue event id 10002 event 0 file two.txt
> > > > >  Event id=1 event=0 file=two.txt
> > > > >  Expected event 0 but got 2
> > > 
> > > Interesting. So In the "Rename" test, the destination already
> > > exists.
> > > 
> > > BSD is thus reporting that 'two.txt' is deleted, before being
> > > (re)created
> > > Linux is only reporting 'two.txt' is created.
> > > 
> > > I don't think we can easily paper over this difference. The
> > > easiest is
> > > probably to conditionalize the test
> > > 
> > >  git diff
> > > diff --git a/tests/unit/test-util-filemonitor.c
> > > b/tests/unit/test-util-filemonitor.c
> > > index a22de27595..c3b2006365 100644
> > > --- a/tests/unit/test-util-filemonitor.c
> > > +++ b/tests/unit/test-util-filemonitor.c
> > > @@ -281,6 +281,14 @@ test_file_monitor_events(void)
> > >  { .type = QFILE_MONITOR_TEST_OP_EVENT,
> > >    .filesrc = "one.txt", .watchid = ,
> > >    .eventid = QFILE_MONITOR_EVENT_DELETED },
> > > +#ifdef __FreeBSD__
> > > +    { .type = QFILE_MONITOR_TEST_OP_EVENT,
> > > +  .filesrc = "two.txt", .watchid = ,
> > > +  .eventid = QFILE_MONITOR_EVENT_DELETED },
> > > +    { .type = QFILE_MONITOR_TEST_OP_EVENT,
> > > +  .filesrc = "two.txt", .watchid = ,
> > > +  .eventid = QFILE_MONITOR_EVENT_DELETED },
> > > +#endif
> > >  { .type = QFILE_MONITOR_TEST_OP_EVENT,
> > >    .filesrc = "two.txt", .watchid = ,
> > >    .eventid = QFILE_MONITOR_EVENT_CREATED },
> > > 
> > 
> > I agree this is likely the best course of action. Has anybody filed
> > a bug
> > at https://bugs.freebsd.org?
> 
> I've not, and I'm not even sure I would class it a FreeBSD bug. Other
> than the fact that it differs from Linux behaviour, it feels like it
> is reasonble semantics to emit a 'delete' event in this scenario so
> that an event consumer can detect replacement of an existing file.
> 
> With regards,
> Daniel

Sounds reasonable; I will send a v2 with the meson adjustments and with
the test fix.



[PATCH v2 09/11] gdbstub: Introduce gdb_handle_detach_user()

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support needs to perform certain
actions when GDB detaches from the stopped parent or the stopped child.
Introduce a user-specific hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 6 ++
 gdbstub/internals.h | 1 +
 gdbstub/user.c  | 5 +
 3 files changed, 12 insertions(+)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index adcd977cd57..46f5dd47e9e 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -991,6 +991,12 @@ static void handle_detach(GArray *params, void *user_ctx)
 pid = get_param(params, 0)->val_ul;
 }
 
+#ifdef CONFIG_USER_ONLY
+if (gdb_handle_detach_user(pid)) {
+return;
+}
+#endif
+
 process = gdb_get_process(pid);
 gdb_process_breakpoint_remove_all(process);
 process->attached = false;
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index b4905c7181a..b4724598384 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -198,6 +198,7 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
 bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */
+bool gdb_handle_detach_user(uint32_t pid); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index ee6b47b9b9c..6f108e60839 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -391,6 +391,11 @@ bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid)
 return false;
 }
 
+bool gdb_handle_detach_user(uint32_t pid)
+{
+return false;
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.0




[PATCH v2 08/11] gdbstub: Introduce gdb_handle_set_thread_user()

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support needs to perform certain
actions when GDB switches between the stopped parent and the stopped
child. Introduce a user-specific hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 11 +--
 gdbstub/internals.h |  1 +
 gdbstub/user.c  |  5 +
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 43d79dfdd59..adcd977cd57 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1066,6 +1066,7 @@ static void handle_cont_with_sig(GArray *params, void 
*user_ctx)
 
 static void handle_set_thread(GArray *params, void *user_ctx)
 {
+uint32_t pid, tid;
 CPUState *cpu;
 
 if (params->len != 2) {
@@ -1083,8 +1084,14 @@ static void handle_set_thread(GArray *params, void 
*user_ctx)
 return;
 }
 
-cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
-  get_param(params, 1)->thread_id.tid);
+pid = get_param(params, 1)->thread_id.pid;
+tid = get_param(params, 1)->thread_id.tid;
+#ifdef CONFIG_USER_ONLY
+if (gdb_handle_set_thread_user(pid, tid)) {
+return;
+}
+#endif
+cpu = gdb_get_cpu(pid, tid);
 if (!cpu) {
 gdb_put_packet("E22");
 return;
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index e6063835b1f..b4905c7181a 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -197,6 +197,7 @@ void gdb_handle_v_file_readlink(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user 
*/
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
+bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 2b8c67972c0..ee6b47b9b9c 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -386,6 +386,11 @@ void gdb_handle_query_supported_user(const char 
*gdb_supported)
 {
 }
 
+bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid)
+{
+return false;
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.0




[PATCH v2 04/11] {linux,bsd}-user: Pass pid to fork_end()

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing the child
pid. Pass it down.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/freebsd/os-proc.h  | 6 +++---
 bsd-user/main.c | 4 +++-
 bsd-user/qemu.h | 2 +-
 linux-user/main.c   | 4 +++-
 linux-user/syscall.c| 6 +++---
 linux-user/user-internals.h | 2 +-
 6 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h
index d6418780344..3003c8cb637 100644
--- a/bsd-user/freebsd/os-proc.h
+++ b/bsd-user/freebsd/os-proc.h
@@ -208,7 +208,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
  */
 set_second_rval(cpu_env, child_flag);
 
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 }
@@ -252,7 +252,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, 
abi_long flags)
  * value: 0 for parent process, 1 for child process.
  */
 set_second_rval(cpu_env, child_flag);
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 
@@ -285,7 +285,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, 
abi_ulong target_fdp,
  * value: 0 for parent process, 1 for child process.
  */
 set_second_rval(cpu_env, child_flag);
-fork_end(child_flag);
+fork_end(ret);
 
 return ret;
 }
diff --git a/bsd-user/main.c b/bsd-user/main.c
index bfe6888ea89..bc233a85cef 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -109,8 +109,10 @@ void fork_start(void)
 gdbserver_fork_start();
 }
 
-void fork_end(int child)
+void fork_end(abi_long pid)
 {
+int child = pid == 0;
+
 if (child) {
 CPUState *cpu, *next_cpu;
 /*
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index dc842fffa7d..2414a87559b 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -180,7 +180,7 @@ void cpu_loop(CPUArchState *env);
 char *target_strerror(int err);
 int get_osversion(void);
 void fork_start(void);
-void fork_end(int child);
+void fork_end(abi_long pid);
 
 #include "qemu/log.h"
 
diff --git a/linux-user/main.c b/linux-user/main.c
index 8c7bea1c631..f1a0267816b 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -147,8 +147,10 @@ void fork_start(void)
 gdbserver_fork_start();
 }
 
-void fork_end(int child)
+void fork_end(abi_long pid)
 {
+int child = pid == 0;
+
 qemu_plugin_user_postfork(child);
 mmap_fork_end(child);
 if (child) {
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index aeb95644873..9e628a0f975 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -6669,7 +6669,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, 
abi_ulong newsp,
 if (ret == 0) {
 /* Child Process.  */
 cpu_clone_regs_child(env, newsp, flags);
-fork_end(1);
+fork_end(ret);
 /* There is a race condition here.  The parent process could
theoretically read the TID in the child process before the child
tid is set.  This would require using either ptrace
@@ -6700,8 +6700,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, 
abi_ulong newsp,
 }
 #endif
 put_user_u32(pid_fd, parent_tidptr);
-}
-fork_end(0);
+}
+fork_end(ret);
 }
 g_assert(!cpu_in_exclusive_context(cpu));
 }
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index c63ef45fc78..9014014d920 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -71,7 +71,7 @@ const char *target_strerror(int err);
 int get_osversion(void);
 void init_qemu_uname_release(void);
 void fork_start(void);
-void fork_end(int child);
+void fork_end(abi_long pid);
 
 /**
  * probe_guest_base:
-- 
2.43.0




[PATCH v2 03/11] gdbstub: Introduce gdbserver_fork_start()

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing when
fork() is about to happen in order to initialize its state. Add a hook
for that.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 1 +
 gdbstub/user.c | 4 
 include/gdbstub/user.h | 5 +
 linux-user/main.c  | 1 +
 4 files changed, 11 insertions(+)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index 4140edc8311..bfe6888ea89 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -106,6 +106,7 @@ void fork_start(void)
 start_exclusive();
 cpu_list_lock();
 mmap_fork_start();
+gdbserver_fork_start();
 }
 
 void fork_end(int child)
diff --git a/gdbstub/user.c b/gdbstub/user.c
index e6809da2243..85fd44b6aa9 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -356,6 +356,10 @@ int gdbserver_start(const char *port_or_path)
 return -1;
 }
 
+void gdbserver_fork_start(void)
+{
+}
+
 static void disable_gdbstub(void)
 {
 CPUState *cpu;
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index 68b6534130c..e33f8d9a9a6 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -45,6 +45,11 @@ static inline int gdb_handlesig(CPUState *cpu, int sig)
  */
 void gdb_signalled(CPUArchState *as, int sig);
 
+/**
+ * gdbserver_fork_start() - inform gdb of the upcoming fork()
+ */
+void gdbserver_fork_start(void);
+
 /**
  * gdbserver_fork() - disable gdb stub for child processes.
  * @cs: CPU
diff --git a/linux-user/main.c b/linux-user/main.c
index e6427d72332..8c7bea1c631 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -144,6 +144,7 @@ void fork_start(void)
 mmap_fork_start();
 cpu_list_lock();
 qemu_plugin_user_prefork_lock();
+gdbserver_fork_start();
 }
 
 void fork_end(int child)
-- 
2.43.0




[PATCH v2 02/11] {linux,bsd}-user: Update ts_tid after fork()

2024-02-05 Thread Ilya Leoshkevich
Currently ts_tid contains the parent tid after fork(), which is not
correct. So far it has not affected anything, but the upcoming
follow-fork-mode child support relies on the correct value, so fix it.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c   | 1 +
 linux-user/main.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index e5efb7b8458..4140edc8311 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -127,6 +127,7 @@ void fork_end(int child)
  * state, so we don't need to end_exclusive() here.
  */
 qemu_init_cpu_list();
+((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
 gdbserver_fork(thread_cpu);
 } else {
 mmap_fork_end(child);
diff --git a/linux-user/main.c b/linux-user/main.c
index 74b2fbb3938..e6427d72332 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -160,6 +160,7 @@ void fork_end(int child)
 }
 }
 qemu_init_cpu_list();
+((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
 gdbserver_fork(thread_cpu);
 } else {
 cpu_list_unlock();
-- 
2.43.0




[PATCH v2 05/11] {linux,bsd}-user: Pass pid to gdbserver_fork()

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires knowing the child
pid. Pass it down.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 2 +-
 gdbstub/user.c | 2 +-
 include/gdbstub/user.h | 2 +-
 linux-user/main.c  | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index bc233a85cef..e8c658eda5d 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -131,7 +131,7 @@ void fork_end(abi_long pid)
  */
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu);
+gdbserver_fork(pid);
 } else {
 mmap_fork_end(child);
 cpu_list_unlock();
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 85fd44b6aa9..d3a749f3e7e 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -375,7 +375,7 @@ static void disable_gdbstub(void)
 }
 
 /* Disable gdb stub for child processes.  */
-void gdbserver_fork(CPUState *cpu)
+void gdbserver_fork(pid_t pid)
 {
 if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index e33f8d9a9a6..66dd0d319cf 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -54,7 +54,7 @@ void gdbserver_fork_start(void);
  * gdbserver_fork() - disable gdb stub for child processes.
  * @cs: CPU
  */
-void gdbserver_fork(CPUState *cs);
+void gdbserver_fork(pid_t pid);
 
 /**
  * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
diff --git a/linux-user/main.c b/linux-user/main.c
index f1a0267816b..ad1c6394520 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -164,7 +164,7 @@ void fork_end(abi_long pid)
 }
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(thread_cpu);
+gdbserver_fork(pid);
 } else {
 cpu_list_unlock();
 }
-- 
2.43.0




[PATCH v2 10/11] gdbstub: Implement follow-fork-mode child

2024-02-05 Thread Ilya Leoshkevich
Currently it's not possible to use gdbstub for debugging linux-user
code that runs in a forked child, which is normally done using the `set
follow-fork-mode child` GDB command. Purely on the protocol level, the
missing piece is the fork-events feature.

However, a deeper problem is supporting $Hg switching between different
processes - right now it can do only threads. Implementing this for the
general case would be quite complicated, but, fortunately, for the
follow-fork-mode case there are a few factors that greatly simplify
things: fork() happens in the exclusive section, there are only two
processes involved, and before one of them is resumed, the second one
is detached.

This makes it possible to implement a simplified scheme: the parent and
the child share the gdbserver socket, it's used only by one of them at
any given time, which is coordinated through a separate socketpair. The
processes can read from the gdbserver socket only one byte at a time,
which is not great for performance, but, fortunately, the
follow-fork-mode handling involves only a few messages.

Advertise the fork-events support, and remember whether GDB has it
as well. Implement the state machine that is initialized on fork(),
decides the current owner of the gdbserver socket, and is terminated
when one of the two processes is detached. The logic for the parent and
the child is the same, only the initial state is different.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c | 212 -
 1 file changed, 210 insertions(+), 2 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 6f108e60839..bb1e384ddb1 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -25,6 +25,61 @@
 #define GDB_NR_SYSCALLS 1024
 typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)];
 
+/*
+ * Forked child talks to its parent in order to let GDB enforce the
+ * follow-fork-mode. This happens inside a start_exclusive() section, so that
+ * the other threads, which may be forking too, do not interfere. The
+ * implementation relies on GDB not sending $vCont until it has detached
+ * either from the parent (follow-fork-mode child) or from the child
+ * (follow-fork-mode parent).
+ *
+ * The parent and the child share the GDB socket; at any given time only one
+ * of them is allowed to use it, as is reflected in the respective fork_state.
+ * This is negotiated via the fork_sockets pair as a reaction to $Hg.
+ *
+ * Below is a short summary of the possible state transitions:
+ *
+ * ENABLED : Terminal state.
+ * DISABLED: Terminal state.
+ * ACTIVE  : Parent initial state.
+ * INACTIVE: Child initial state.
+ * ACTIVE   -> DEACTIVATING: On $Hg.
+ * ACTIVE   -> ENABLING: On $D.
+ * ACTIVE   -> DISABLING   : On $D.
+ * ACTIVE   -> DISABLED: On communication error.
+ * DEACTIVATING -> INACTIVE: On gdb_read_byte() return.
+ * DEACTIVATING -> DISABLED: On communication error.
+ * INACTIVE -> ACTIVE  : On $Hg in the peer.
+ * INACTIVE -> ENABLE  : On $D in the peer.
+ * INACTIVE -> DISABLE : On $D in the peer.
+ * INACTIVE -> DISABLED: On communication error.
+ * ENABLING -> ENABLED : On gdb_read_byte() return.
+ * ENABLING -> DISABLED: On communication error.
+ * DISABLING-> DISABLED: On gdb_read_byte() return.
+ */
+enum GDBForkState {
+/* Fully owning the GDB socket. */
+GDB_FORK_ENABLED,
+/* Working with the GDB socket; the peer is inactive. */
+GDB_FORK_ACTIVE,
+/* Handing off the GDB socket to the peer. */
+GDB_FORK_DEACTIVATING,
+/* The peer is working with the GDB socket. */
+GDB_FORK_INACTIVE,
+/* Asking the peer to close its GDB socket fd. */
+GDB_FORK_ENABLING,
+/* Asking the peer to take over, closing our GDB socket fd. */
+GDB_FORK_DISABLING,
+/* The peer has taken over, our GDB socket fd is closed. */
+GDB_FORK_DISABLED,
+};
+
+enum GDBForkMessage {
+GDB_FORK_ACTIVATE = 'a',
+GDB_FORK_ENABLE = 'e',
+GDB_FORK_DISABLE = 'd',
+};
+
 /* User-mode specific state */
 typedef struct {
 int fd;
@@ -36,6 +91,10 @@ typedef struct {
  */
 bool catch_all_syscalls;
 GDBSyscallsMask catch_syscalls_mask;
+bool fork_events;
+enum GDBForkState fork_state;
+int fork_sockets[2];
+pid_t fork_peer_pid, fork_peer_tid;
 } GDBUserState;
 
 static GDBUserState gdbserver_user_state;
@@ -358,6 +417,18 @@ int gdbserver_start(const char *port_or_path)
 
 void gdbserver_fork_start(void)
 {
+if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+return;
+}
+if (!gdbserver_user_state.fork_events ||
+qemu_socketpair(AF_UNIX, SOCK_STREAM, 0,
+gdbserver_user_state.fork_sockets) < 

[PATCH v2 11/11] tests/tcg: Add two follow-fork-mode tests

2024-02-05 Thread Ilya Leoshkevich
Add follow-fork-mode child and and follow-fork-mode parent tests.
Check for the obvious pitfalls, such as lingering breakpoints,
catchpoints, and single-step mode.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/Makefile.target   | 17 +-
 tests/tcg/multiarch/follow-fork-mode.c| 56 +++
 .../gdbstub/follow-fork-mode-child.py | 40 +
 .../gdbstub/follow-fork-mode-parent.py| 16 ++
 4 files changed, 128 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/multiarch/follow-fork-mode.c
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py

diff --git a/tests/tcg/multiarch/Makefile.target 
b/tests/tcg/multiarch/Makefile.target
index e10951a8016..b8b70c81860 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -115,6 +115,20 @@ run-gdbstub-catch-syscalls: catch-syscalls
--bin $< --test $(MULTIARCH_SRC)/gdbstub/catch-syscalls.py, \
hitting a syscall catchpoint)
 
+run-gdbstub-follow-fork-mode-child: follow-fork-mode
+   $(call run-test, $@, $(GDB_SCRIPT) \
+   --gdb $(GDB) \
+   --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+   --bin $< --test 
$(MULTIARCH_SRC)/gdbstub/follow-fork-mode-child.py, \
+   following children on fork)
+
+run-gdbstub-follow-fork-mode-parent: follow-fork-mode
+   $(call run-test, $@, $(GDB_SCRIPT) \
+   --gdb $(GDB) \
+   --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+   --bin $< --test 
$(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \
+   following parents on fork)
+
 else
 run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst 
-%,,$(TARGET_NAME)) support")
@@ -122,7 +136,8 @@ endif
 EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
  run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \
  run-gdbstub-registers run-gdbstub-prot-none \
- run-gdbstub-catch-syscalls
+ run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \
+ run-gdbstub-follow-fork-mode-parent
 
 # ARM Compatible Semi Hosting Tests
 #
diff --git a/tests/tcg/multiarch/follow-fork-mode.c 
b/tests/tcg/multiarch/follow-fork-mode.c
new file mode 100644
index 000..cb6b032b388
--- /dev/null
+++ b/tests/tcg/multiarch/follow-fork-mode.c
@@ -0,0 +1,56 @@
+/*
+ * Test GDB's follow-fork-mode.
+ *
+ * fork() a chain of processes.
+ * Parents sends one byte to their children, and children return their
+ * position in the chain, in order to prove that they survived GDB's fork()
+ * handling.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+
+void break_after_fork(void)
+{
+}
+
+int main(void)
+{
+int depth = 42, err, i, fd[2], status;
+pid_t child, pid;
+ssize_t n;
+char b;
+
+for (i = 0; i < depth; i++) {
+err = pipe(fd);
+assert(err == 0);
+child = fork();
+break_after_fork();
+assert(child != -1);
+if (child == 0) {
+close(fd[1]);
+
+n = read(fd[0], , 1);
+close(fd[0]);
+assert(n == 1);
+assert(b == (char)i);
+} else {
+close(fd[0]);
+
+b = (char)i;
+n = write(fd[1], , 1);
+close(fd[1]);
+assert(n == 1);
+
+pid = waitpid(child, , 0);
+assert(pid == child);
+assert(WIFEXITED(status));
+return WEXITSTATUS(status) - 1;
+}
+}
+
+return depth;
+}
diff --git a/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py 
b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
new file mode 100644
index 000..72a6e440c08
--- /dev/null
+++ b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
@@ -0,0 +1,40 @@
+"""Test GDB's follow-fork-mode child.
+
+SPDX-License-Identifier: GPL-2.0-or-later
+"""
+from test_gdbstub import main, report
+
+
+def run_test():
+"""Run through the tests one by one"""
+gdb.execute("set follow-fork-mode child")
+# Check that the parent breakpoints are unset.
+gdb.execute("break break_after_fork")
+# Check that the parent syscall catchpoints are unset.
+# Skip this check on the architectures that don't have them.
+have_fork_syscall = False
+for fork_syscall in ("fork", "clone", "clone2", "clone3"):
+try:
+gdb.execute("catch syscall {}".format(fork_syscall))
+except gdb.error:
+pass
+else:
+have_fork_syscall = True
+gdb.execute("continue")
+for i in range(42):
+

[PATCH v2 00/11] gdbstub: Implement follow-fork-mode child

2024-02-05 Thread Ilya Leoshkevich
Based-on: <20240202152506.279476-1-...@linux.ibm.com>
("[PATCH v3 0/5] gdbstub: Implement catching syscalls")

v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06646.html
v1 -> v2: Factor out a number of prep patches;
  Add a state transition diagram comment (Alex).
  Improve a few comments;
  Extend the ts_tid fix to bsd.

Hi,

I needed to debug a linux-user crash between fork() and exec() [1] and
realized that gdbstub does not allow this. This series lifts this
restriction (one still cannot debug past exec() though). Patches 1-9
are preliminary refactorings, patch 10 is the implementation, and patch
11 is the test.

[1] https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06424.html

Best regards,
Ilya

Ilya Leoshkevich (11):
  gdbstub: Support disablement in a multi-threaded process
  {linux,bsd}-user: Update ts_tid after fork()
  gdbstub: Introduce gdbserver_fork_start()
  {linux,bsd}-user: Pass pid to fork_end()
  {linux,bsd}-user: Pass pid to gdbserver_fork()
  gdbstub: Call gdbserver_fork() both in parent and in child
  gdbstub: Introduce gdb_handle_query_supported_user()
  gdbstub: Introduce gdb_handle_set_thread_user()
  gdbstub: Introduce gdb_handle_detach_user()
  gdbstub: Implement follow-fork-mode child
  tests/tcg: Add two follow-fork-mode tests

 bsd-user/freebsd/os-proc.h|   6 +-
 bsd-user/main.c   |   9 +-
 bsd-user/qemu.h   |   2 +-
 gdbstub/gdbstub.c |  29 ++-
 gdbstub/internals.h   |   3 +
 gdbstub/user.c| 244 +-
 include/gdbstub/user.h|  11 +-
 linux-user/main.c |   8 +-
 linux-user/syscall.c  |   6 +-
 linux-user/user-internals.h   |   2 +-
 tests/tcg/multiarch/Makefile.target   |  17 +-
 tests/tcg/multiarch/follow-fork-mode.c|  56 
 .../gdbstub/follow-fork-mode-child.py |  40 +++
 .../gdbstub/follow-fork-mode-parent.py|  16 ++
 14 files changed, 424 insertions(+), 25 deletions(-)
 create mode 100644 tests/tcg/multiarch/follow-fork-mode.c
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py
 create mode 100644 tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py

-- 
2.43.0




[PATCH v2 06/11] gdbstub: Call gdbserver_fork() both in parent and in child

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires post-fork message
exchange between the parent and the child. Prepare gdbserver_fork() for
this purpose. Rename it to gdbserver_fork_end() to better reflect its
purpose.

Signed-off-by: Ilya Leoshkevich 
---
 bsd-user/main.c| 3 ++-
 gdbstub/user.c | 5 ++---
 include/gdbstub/user.h | 6 +++---
 linux-user/main.c  | 2 +-
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/bsd-user/main.c b/bsd-user/main.c
index e8c658eda5d..1890d6365f7 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -131,10 +131,11 @@ void fork_end(abi_long pid)
  */
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(pid);
+gdbserver_fork_end(pid);
 } else {
 mmap_fork_end(child);
 cpu_list_unlock();
+gdbserver_fork_end(pid);
 end_exclusive();
 }
 }
diff --git a/gdbstub/user.c b/gdbstub/user.c
index d3a749f3e7e..df5a618e789 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -374,10 +374,9 @@ static void disable_gdbstub(void)
 }
 }
 
-/* Disable gdb stub for child processes.  */
-void gdbserver_fork(pid_t pid)
+void gdbserver_fork_end(pid_t pid)
 {
-if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
+if (pid != 0 || !gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
 }
 disable_gdbstub();
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index 66dd0d319cf..dd03dbdd6df 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -51,10 +51,10 @@ void gdb_signalled(CPUArchState *as, int sig);
 void gdbserver_fork_start(void);
 
 /**
- * gdbserver_fork() - disable gdb stub for child processes.
- * @cs: CPU
+ * gdbserver_fork_end() - inform gdb of the completed fork()
+ * @pid: 0 if in child process, -1 if fork failed, child process pid otherwise
  */
-void gdbserver_fork(pid_t pid);
+void gdbserver_fork_end(pid_t pid);
 
 /**
  * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
diff --git a/linux-user/main.c b/linux-user/main.c
index ad1c6394520..dde5081e2f4 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -164,10 +164,10 @@ void fork_end(abi_long pid)
 }
 qemu_init_cpu_list();
 ((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
-gdbserver_fork(pid);
 } else {
 cpu_list_unlock();
 }
+gdbserver_fork_end(pid);
 /*
  * qemu_init_cpu_list() reinitialized the child exclusive state, but we
  * also need to keep current_cpu consistent, so call end_exclusive() for
-- 
2.43.0




[PATCH v2 07/11] gdbstub: Introduce gdb_handle_query_supported_user()

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support requires advertising the
fork-events feature, which is user-specific. Introduce a user-specific
hook for this.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   | 12 +---
 gdbstub/internals.h |  1 +
 gdbstub/user.c  |  4 
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 7e73e916bdc..43d79dfdd59 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1599,6 +1599,7 @@ static void handle_query_thread_extra(GArray *params, 
void *user_ctx)
 
 static void handle_query_supported(GArray *params, void *user_ctx)
 {
+const char *gdb_supported;
 CPUClass *cc;
 
 g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", 
MAX_PACKET_LENGTH);
@@ -1622,9 +1623,14 @@ static void handle_query_supported(GArray *params, void 
*user_ctx)
 g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+");
 #endif
 
-if (params->len &&
-strstr(get_param(params, 0)->data, "multiprocess+")) {
-gdbserver_state.multiprocess = true;
+if (params->len) {
+gdb_supported = get_param(params, 0)->data;
+if (strstr(gdb_supported, "multiprocess+")) {
+gdbserver_state.multiprocess = true;
+}
+#if defined(CONFIG_USER_ONLY)
+gdb_handle_query_supported_user(gdb_supported);
+#endif
 }
 
 g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index 56b7c13b750..e6063835b1f 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -196,6 +196,7 @@ void gdb_handle_v_file_pread(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user 
*/
 void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
+void gdb_handle_query_supported_user(const char *gdb_supported); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index df5a618e789..2b8c67972c0 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -382,6 +382,10 @@ void gdbserver_fork_end(pid_t pid)
 disable_gdbstub();
 }
 
+void gdb_handle_query_supported_user(const char *gdb_supported)
+{
+}
+
 /*
  * Execution state helpers
  */
-- 
2.43.0




[PATCH v2 01/11] gdbstub: Support disablement in a multi-threaded process

2024-02-05 Thread Ilya Leoshkevich
The upcoming follow-fork-mode child support will require disabling
gdbstub in the parent process, which may have multiple threads (which
are represented as CPUs).

Loop over all CPUs in order to remove watchpoints and disable
single-step. Move the respective code into a separate function.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c | 19 +++
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 8f3affbad47..e6809da2243 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -356,16 +356,27 @@ int gdbserver_start(const char *port_or_path)
 return -1;
 }
 
+static void disable_gdbstub(void)
+{
+CPUState *cpu;
+
+close(gdbserver_user_state.fd);
+gdbserver_user_state.fd = -1;
+CPU_FOREACH(cpu) {
+cpu_breakpoint_remove_all(cpu, BP_GDB);
+/* no cpu_watchpoint_remove_all for user-mode */
+cpu_single_step(cpu, 0);
+tb_flush(cpu);
+}
+}
+
 /* Disable gdb stub for child processes.  */
 void gdbserver_fork(CPUState *cpu)
 {
 if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
 return;
 }
-close(gdbserver_user_state.fd);
-gdbserver_user_state.fd = -1;
-cpu_breakpoint_remove_all(cpu, BP_GDB);
-/* no cpu_watchpoint_remove_all for user-mode */
+disable_gdbstub();
 }
 
 /*
-- 
2.43.0




Re: Re: [PATCH] tests/tcg: Fix the /proc/self/mem probing in the PROT_NONE gdbstub test

2024-02-05 Thread Ilya Leoshkevich
On Sat, Feb 03, 2024 at 11:48:44PM +0300, Michael Tokarev wrote:
> 01.02.2024 01:02, Ilya Leoshkevich wrote:
> > The `if not probe_proc_self_mem` check never passes, because
> > probe_proc_self_mem is a function object, which is a truthy value.
> > Add parentheses in order to perform a function call.
> > 
> > Fixes: dc84d50a7f9b ("tests/tcg: Add the PROT_NONE gdbstub test")
> 
> FWIW (it's too late already and this commit has landed in master),
> commit "tests/tcg: Add the PROT_NONE gdbstub test" is 82607a73f8
> not dc84d50a7f9b.
> 
> /mjt

Sorry about that; I thought that checkpatch catches such issues and
didn't double check - but apparently this is a relatively new addition
to the kernel checkpatch, which did not find its way into the qemu
checkpatch yet.



[PATCH v3 4/5] gdbstub: Implement catching syscalls

2024-02-02 Thread Ilya Leoshkevich
GDB supports stopping on syscall entry and exit using the "catch
syscall" command. It relies on 3 packets, which are currently not
supported by QEMU:

* qSupported:QCatchSyscalls+ [1]
* QCatchSyscalls: [2]
* T05syscall_entry: and T05syscall_return: [3]

Implement generation and handling of these packets.

[1] 
https://sourceware.org/gdb/current/onlinedocs/gdb.html/General-Query-Packets.html#qSupported
[2] 
https://sourceware.org/gdb/current/onlinedocs/gdb.html/General-Query-Packets.html#QCatchSyscalls
[3] 
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Stop-Reply-Packets.html

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/gdbstub.c   |  9 +
 gdbstub/internals.h |  1 +
 gdbstub/user.c  | 95 +
 3 files changed, 105 insertions(+)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 46d752bbc2c..7e73e916bdc 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1617,6 +1617,7 @@ static void handle_query_supported(GArray *params, void 
*user_ctx)
 if (gdbserver_state.c_cpu->opaque) {
 g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+");
 }
+g_string_append(gdbserver_state.str_buf, ";QCatchSyscalls+");
 #endif
 g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+");
 #endif
@@ -1810,6 +1811,14 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = {
 .schema = "l0"
 },
 #endif
+#if defined(CONFIG_USER_ONLY)
+{
+.handler = gdb_handle_set_catch_syscalls,
+.cmd = "CatchSyscalls:",
+.cmd_startswith = 1,
+.schema = "s0",
+},
+#endif
 };
 
 static void handle_gen_query(GArray *params, void *user_ctx)
diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index aeb0d9b5377..56b7c13b750 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -195,6 +195,7 @@ void gdb_handle_v_file_close(GArray *params, void 
*user_ctx); /* user */
 void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */
 void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */
 void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user 
*/
+void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */
 
 void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
 
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 2ba01c17faf..8f3affbad47 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -10,6 +10,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/bitops.h"
 #include "qemu/cutils.h"
 #include "qemu/sockets.h"
 #include "exec/hwaddr.h"
@@ -21,11 +22,20 @@
 #include "trace.h"
 #include "internals.h"
 
+#define GDB_NR_SYSCALLS 1024
+typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)];
+
 /* User-mode specific state */
 typedef struct {
 int fd;
 char *socket_path;
 int running_state;
+/*
+ * Store syscalls mask without memory allocation in order to avoid
+ * implementing synchronization.
+ */
+bool catch_all_syscalls;
+GDBSyscallsMask catch_syscalls_mask;
 } GDBUserState;
 
 static GDBUserState gdbserver_user_state;
@@ -503,10 +513,95 @@ void gdb_syscall_handling(const char *syscall_packet)
 gdb_handlesig(gdbserver_state.c_cpu, 0);
 }
 
+static bool should_catch_syscall(int num)
+{
+if (gdbserver_user_state.catch_all_syscalls) {
+return true;
+}
+if (num < 0 || num >= GDB_NR_SYSCALLS) {
+return false;
+}
+return test_bit(num, gdbserver_user_state.catch_syscalls_mask);
+}
+
 void gdb_syscall_entry(CPUState *cs, int num)
 {
+if (should_catch_syscall(num)) {
+g_autoptr(GString) reason = g_string_sized_new(24);
+
+g_string_printf(reason, "syscall_entry:%x;", num);
+gdb_handlesig_reason(cs, gdb_target_sigtrap(), reason->str);
+}
 }
 
 void gdb_syscall_return(CPUState *cs, int num)
 {
+if (should_catch_syscall(num)) {
+g_autoptr(GString) reason = g_string_sized_new(24);
+
+g_string_printf(reason, "syscall_return:%x;", num);
+gdb_handlesig_reason(cs, gdb_target_sigtrap(), reason->str);
+}
+}
+
+void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx)
+{
+const char *param = get_param(params, 0)->data;
+GDBSyscallsMask catch_syscalls_mask;
+bool catch_all_syscalls;
+unsigned int num;
+const char *p;
+
+/* "0" means not catching any syscalls. */
+if (strcmp(param, "0") == 0) {
+gdbserver_user_state.catch_all_syscalls = false;
+memset(gdbserver_user_state.catch_syscalls_mask, 0,
+   sizeof(gdbserver_user_state.catch_syscalls_mask));
+gdb_put_packet("OK");
+return;
+}
+
+/* "1" means catching all syscalls. */
+if (strcmp(param, "

[PATCH v3 5/5] tests/tcg: Add the syscall catchpoint gdbstub test

2024-02-02 Thread Ilya Leoshkevich
Check that adding/removing syscall catchpoints works.

Reviewed-by: Alex Bennée 
Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/multiarch/Makefile.target   | 10 +++-
 tests/tcg/multiarch/catch-syscalls.c  | 51 ++
 tests/tcg/multiarch/gdbstub/catch-syscalls.py | 53 +++
 3 files changed, 113 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/multiarch/catch-syscalls.c
 create mode 100644 tests/tcg/multiarch/gdbstub/catch-syscalls.py

diff --git a/tests/tcg/multiarch/Makefile.target 
b/tests/tcg/multiarch/Makefile.target
index 315a2e13588..e10951a8016 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -108,13 +108,21 @@ run-gdbstub-prot-none: prot-none
--bin $< --test $(MULTIARCH_SRC)/gdbstub/prot-none.py, \
accessing PROT_NONE memory)
 
+run-gdbstub-catch-syscalls: catch-syscalls
+   $(call run-test, $@, $(GDB_SCRIPT) \
+   --gdb $(GDB) \
+   --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
+   --bin $< --test $(MULTIARCH_SRC)/gdbstub/catch-syscalls.py, \
+   hitting a syscall catchpoint)
+
 else
 run-gdbstub-%:
$(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst 
-%,,$(TARGET_NAME)) support")
 endif
 EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \
  run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \
- run-gdbstub-registers run-gdbstub-prot-none
+ run-gdbstub-registers run-gdbstub-prot-none \
+ run-gdbstub-catch-syscalls
 
 # ARM Compatible Semi Hosting Tests
 #
diff --git a/tests/tcg/multiarch/catch-syscalls.c 
b/tests/tcg/multiarch/catch-syscalls.c
new file mode 100644
index 000..d1ff1936a7a
--- /dev/null
+++ b/tests/tcg/multiarch/catch-syscalls.c
@@ -0,0 +1,51 @@
+/*
+ * Test GDB syscall catchpoints.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#define _GNU_SOURCE
+#include 
+#include 
+
+const char *catch_syscalls_state = "start";
+
+void end_of_main(void)
+{
+}
+
+int main(void)
+{
+int ret = EXIT_FAILURE;
+char c0 = 'A', c1;
+int fd[2];
+
+catch_syscalls_state = "pipe2";
+if (pipe2(fd, 0)) {
+goto out;
+}
+
+catch_syscalls_state = "write";
+if (write(fd[1], , sizeof(c0)) != sizeof(c0)) {
+goto out_close;
+}
+
+catch_syscalls_state = "read";
+if (read(fd[0], , sizeof(c1)) != sizeof(c1)) {
+goto out_close;
+}
+
+catch_syscalls_state = "check";
+if (c0 == c1) {
+ret = EXIT_SUCCESS;
+}
+
+out_close:
+catch_syscalls_state = "close";
+close(fd[0]);
+close(fd[1]);
+
+out:
+catch_syscalls_state = "end";
+end_of_main();
+return ret;
+}
diff --git a/tests/tcg/multiarch/gdbstub/catch-syscalls.py 
b/tests/tcg/multiarch/gdbstub/catch-syscalls.py
new file mode 100644
index 000..ccce35902fb
--- /dev/null
+++ b/tests/tcg/multiarch/gdbstub/catch-syscalls.py
@@ -0,0 +1,53 @@
+"""Test GDB syscall catchpoints.
+
+SPDX-License-Identifier: GPL-2.0-or-later
+"""
+from test_gdbstub import main, report
+
+
+def check_state(expected):
+"""Check the catch_syscalls_state value"""
+actual = gdb.parse_and_eval("catch_syscalls_state").string()
+report(actual == expected, "{} == {}".format(actual, expected))
+
+
+def run_test():
+"""Run through the tests one by one"""
+gdb.Breakpoint("main")
+gdb.execute("continue")
+
+# Check that GDB stops for pipe2/read calls/returns, but not for write.
+gdb.execute("delete")
+try:
+gdb.execute("catch syscall pipe2 read")
+except gdb.error as exc:
+exc_str = str(exc)
+if "not supported on this architecture" in exc_str:
+print("SKIP: {}".format(exc_str))
+return
+raise
+for _ in range(2):
+gdb.execute("continue")
+check_state("pipe2")
+for _ in range(2):
+gdb.execute("continue")
+check_state("read")
+
+# Check that deletion works.
+gdb.execute("delete")
+gdb.Breakpoint("end_of_main")
+gdb.execute("continue")
+check_state("end")
+
+# Check that catch-all works (libc should at least call exit).
+gdb.execute("delete")
+gdb.execute("catch syscall")
+gdb.execute("continue")
+gdb.execute("delete")
+gdb.execute("continue")
+
+exitcode = int(gdb.parse_and_eval("$_exitcode"))
+report(exitcode == 0, "{} == 0".format(exitcode))
+
+
+main(run_test)
-- 
2.43.0




[PATCH v3 3/5] gdbstub: Add syscall entry/return hooks

2024-02-02 Thread Ilya Leoshkevich
The upcoming syscall catchpoint support needs to get control on syscall
entry and return. Provide the necessary hooks for that, which are
no-ops for now.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c   |  8 
 include/gdbstub/user.h   | 13 +
 include/user/syscall-trace.h |  7 +--
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index 63edca131ab..2ba01c17faf 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -502,3 +502,11 @@ void gdb_syscall_handling(const char *syscall_packet)
 gdb_put_packet(syscall_packet);
 gdb_handlesig(gdbserver_state.c_cpu, 0);
 }
+
+void gdb_syscall_entry(CPUState *cs, int num)
+{
+}
+
+void gdb_syscall_return(CPUState *cs, int num)
+{
+}
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index 1fc43e04af5..68b6534130c 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -51,5 +51,18 @@ void gdb_signalled(CPUArchState *as, int sig);
  */
 void gdbserver_fork(CPUState *cs);
 
+/**
+ * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it
+ * @cs: CPU
+ * @num: syscall number
+ */
+void gdb_syscall_entry(CPUState *cs, int num);
+
+/**
+ * gdb_syscall_entry() - inform gdb of syscall return and yield control to it
+ * @cs: CPU
+ * @num: syscall number
+ */
+void gdb_syscall_return(CPUState *cs, int num);
 
 #endif /* GDBSTUB_USER_H */
diff --git a/include/user/syscall-trace.h b/include/user/syscall-trace.h
index 557f881a79b..b48b2b2d0ae 100644
--- a/include/user/syscall-trace.h
+++ b/include/user/syscall-trace.h
@@ -11,6 +11,7 @@
 #define SYSCALL_TRACE_H
 
 #include "exec/user/abitypes.h"
+#include "gdbstub/user.h"
 #include "qemu/plugin.h"
 #include "trace/trace-root.h"
 
@@ -20,7 +21,7 @@
  * could potentially unify the -strace code here as well.
  */
 
-static inline void record_syscall_start(void *cpu, int num,
+static inline void record_syscall_start(CPUState *cpu, int num,
 abi_long arg1, abi_long arg2,
 abi_long arg3, abi_long arg4,
 abi_long arg5, abi_long arg6,
@@ -29,11 +30,13 @@ static inline void record_syscall_start(void *cpu, int num,
 qemu_plugin_vcpu_syscall(cpu, num,
  arg1, arg2, arg3, arg4,
  arg5, arg6, arg7, arg8);
+gdb_syscall_entry(cpu, num);
 }
 
-static inline void record_syscall_return(void *cpu, int num, abi_long ret)
+static inline void record_syscall_return(CPUState *cpu, int num, abi_long ret)
 {
 qemu_plugin_vcpu_syscall_ret(cpu, num, ret);
+gdb_syscall_return(cpu, num);
 }
 
 
-- 
2.43.0




[PATCH v3 0/5] gdbstub: Implement catching syscalls

2024-02-02 Thread Ilya Leoshkevich
v2: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg02980.html
v2 -> v3: Simplify the catchpoint state by making "don't catch" a
  subset of "catch some".
  Factor out several prep patches;
  Don't use snprintf;
  Add some comments (Alex).

v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg02911.html
v1 -> v2: Avoid touching the system gdbstub.
  Advertise QCatchSyscalls+ only on Linux.

Hi,

I noticed that GDB's "catch syscall" does not work with qemu-user.
This series adds the missing bits in [1/2] and a test in [2/2].
I'm basing this on my other series, since it contains useful gdbstub
test refactorings.

Best regards,
Ilya

Ilya Leoshkevich (5):
  gdbstub: Expose TARGET_SIGTRAP in a target-agnostic way
  gdbstub: Allow specifying a reason in stop packets
  gdbstub: Add syscall entry/return hooks
  gdbstub: Implement catching syscalls
  tests/tcg: Add the syscall catchpoint gdbstub test

 gdbstub/gdbstub.c |   9 ++
 gdbstub/internals.h   |   2 +
 gdbstub/user-target.c |   5 +
 gdbstub/user.c| 108 +-
 include/gdbstub/user.h|  29 -
 include/user/syscall-trace.h  |   7 +-
 tests/tcg/multiarch/Makefile.target   |  10 +-
 tests/tcg/multiarch/catch-syscalls.c  |  51 +
 tests/tcg/multiarch/gdbstub/catch-syscalls.py |  53 +
 9 files changed, 268 insertions(+), 6 deletions(-)
 create mode 100644 tests/tcg/multiarch/catch-syscalls.c
 create mode 100644 tests/tcg/multiarch/gdbstub/catch-syscalls.py

-- 
2.43.0




[PATCH v3 1/5] gdbstub: Expose TARGET_SIGTRAP in a target-agnostic way

2024-02-02 Thread Ilya Leoshkevich
The upcoming syscall catchpoint support needs to send SIGTRAP stop
packets to GDB. Being able to compile this support only once for all
targets is a good thing, and it requires hiding TARGET_SIGTRAP behind
a function call.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/internals.h   | 1 +
 gdbstub/user-target.c | 5 +
 2 files changed, 6 insertions(+)

diff --git a/gdbstub/internals.h b/gdbstub/internals.h
index 5c0c725e54c..aeb0d9b5377 100644
--- a/gdbstub/internals.h
+++ b/gdbstub/internals.h
@@ -136,6 +136,7 @@ void gdb_append_thread_id(CPUState *cpu, GString *buf);
 int gdb_get_cpu_index(CPUState *cpu);
 unsigned int gdb_get_max_cpus(void); /* both */
 bool gdb_can_reverse(void); /* softmmu, stub for user */
+int gdb_target_sigtrap(void); /* user */
 
 void gdb_create_default_process(GDBState *s);
 
diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
index c4bba4c72c7..b7d4c37cd81 100644
--- a/gdbstub/user-target.c
+++ b/gdbstub/user-target.c
@@ -418,3 +418,8 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void 
*user_ctx)
 ts->bprm->filename + offset);
 gdb_put_strbuf();
 }
+
+int gdb_target_sigtrap(void)
+{
+return TARGET_SIGTRAP;
+}
-- 
2.43.0




[PATCH v3 2/5] gdbstub: Allow specifying a reason in stop packets

2024-02-02 Thread Ilya Leoshkevich
The upcoming syscall catchpoint support needs to send stop packets with
an associated reason to GDB. Add an extra parameter to gdb_handlesig()
for that, and rename it to gdb_handlesig_reason(). Provide a
compatibility wrapper with an old name.

Signed-off-by: Ilya Leoshkevich 
---
 gdbstub/user.c |  5 -
 include/gdbstub/user.h | 16 ++--
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/gdbstub/user.c b/gdbstub/user.c
index dbe1d9b8875..63edca131ab 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -121,7 +121,7 @@ void gdb_qemu_exit(int code)
 exit(code);
 }
 
-int gdb_handlesig(CPUState *cpu, int sig)
+int gdb_handlesig_reason(CPUState *cpu, int sig, const char *reason)
 {
 char buf[256];
 int n;
@@ -141,6 +141,9 @@ int gdb_handlesig(CPUState *cpu, int sig)
 "T%02xthread:", gdb_target_signal_to_gdb(sig));
 gdb_append_thread_id(cpu, gdbserver_state.str_buf);
 g_string_append_c(gdbserver_state.str_buf, ';');
+if (reason) {
+g_string_append(gdbserver_state.str_buf, reason);
+}
 gdb_put_strbuf();
 gdbserver_state.allow_stop_reply = false;
 }
diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h
index d392e510c59..1fc43e04af5 100644
--- a/include/gdbstub/user.h
+++ b/include/gdbstub/user.h
@@ -10,9 +10,10 @@
 #define GDBSTUB_USER_H
 
 /**
- * gdb_handlesig() - yield control to gdb
+ * gdb_handlesig_reason() - yield control to gdb
  * @cpu: CPU
  * @sig: if non-zero, the signal number which caused us to stop
+ * @reason: stop reason for stop reply packet or NULL
  *
  * This function yields control to gdb, when a user-mode-only target
  * needs to stop execution. If @sig is non-zero, then we will send a
@@ -24,7 +25,18 @@
  * or 0 if no signal should be delivered, ie the signal that caused
  * us to stop should be ignored.
  */
-int gdb_handlesig(CPUState *, int);
+int gdb_handlesig_reason(CPUState *, int, const char *);
+
+/**
+ * gdb_handlesig() - yield control to gdb
+ * @cpu CPU
+ * @sig: if non-zero, the signal number which caused us to stop
+ * @see gdb_handlesig_reason()
+ */
+static inline int gdb_handlesig(CPUState *cpu, int sig)
+{
+return gdb_handlesig_reason(cpu, sig, NULL);
+}
 
 /**
  * gdb_signalled() - inform remote gdb of sig exit
-- 
2.43.0




Re: [PATCH v2 1/2] gdbstub: Implement catching syscalls

2024-02-02 Thread Ilya Leoshkevich
On Thu, 2024-02-01 at 17:15 +, Alex Bennée wrote:
> Ilya Leoshkevich  writes:
> 
> > GDB supports stopping on syscall entry and exit using the "catch
> > syscall" command. It relies on 3 packets, which are currently not
> > supported by QEMU:
> > 
> > * qSupported:QCatchSyscalls+ [1]
> > * QCatchSyscalls: [2]
> > * T05syscall_entry: and T05syscall_return: [3]
> > 
> > Implement generation and handling of these packets.
> > 
> > [1]
> > https://sourceware.org/gdb/current/onlinedocs/gdb.html/General-Query-Packets.html#qSupported
> > [2]
> > https://sourceware.org/gdb/current/onlinedocs/gdb.html/General-Query-Packets.html#QCatchSyscalls
> > [3]
> > https://sourceware.org/gdb/current/onlinedocs/gdb.html/Stop-Reply-Packets.html
> > 
> > Signed-off-by: Ilya Leoshkevich 
> > ---
> >  gdbstub/gdbstub.c    |   9 +++
> >  gdbstub/internals.h  |   2 +
> >  gdbstub/user-target.c    |   5 ++
> >  gdbstub/user.c   | 104
> > ++-
> >  include/gdbstub/user.h   |  29 +-
> >  include/user/syscall-trace.h |   7 ++-
> >  6 files changed, 151 insertions(+), 5 deletions(-)

[...]

> > @@ -499,3 +517,87 @@ void gdb_syscall_handling(const char
> > *syscall_packet)
> >  gdb_put_packet(syscall_packet);
> >  gdb_handlesig(gdbserver_state.c_cpu, 0);
> >  }
> > +
> > +static bool should_catch_syscall(int num)
> > +{
> > +    switch (gdbserver_user_state.catch_syscalls_state) {
> > +    case GDB_CATCH_SYSCALLS_NONE:
> > +    return false;
> > +    case GDB_CATCH_SYSCALLS_ALL:
> > +    return true;
> > +    case GDB_CATCH_SYSCALLS_SELECTED:
> > +    if (num < 0 || num >= GDB_NR_SYSCALLS) {
> > +    return false;
> > +    } else {
> > +    return test_bit(num,
> > gdbserver_user_state.catch_syscalls_mask);
> > +    }
> > +    default:
> > +    g_assert_not_reached();
> > +    }
> > +}
> > +
> > +void gdb_syscall_entry(CPUState *cs, int num)
> > +{
> > +    char reason[32];
> > +
> > +    if (should_catch_syscall(num)) {
> > +    snprintf(reason, sizeof(reason), "syscall_entry:%x;",
> > num);
> > +    gdb_handlesig_reason(cs, gdb_target_sigtrap(), reason);
> > +    }
> > +}
> > +
> > +void gdb_syscall_return(CPUState *cs, int num)
> > +{
> > +    char reason[32];
> > +
> > +    if (should_catch_syscall(num)) {
> > +    snprintf(reason, sizeof(reason), "syscall_return:%x;",
> > num);
> > +    gdb_handlesig_reason(cs, gdb_target_sigtrap(), reason);
> > +    }
> > +}
> 
> I'm not super keen on re-introducing snprintf's as we've been slowly
> eradicating them from the code base. How about:
> 
>     g_autoptr(GString) reason = g_string_printf("syscall_return:%x;",
> num);
>     gdb_handlesig_reason(cs, gdb_target_sigtrap(), reason);

Ok.

> > +
> > +void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx)
> > +{
> > +    enum GDBCatchSyscallsState catch_syscalls_state;
> > +    const char *param = get_param(params, 0)->data;
> > +    GDBSyscallsMask catch_syscalls_mask;
> > +    bool catch_syscalls_none;
> > +    unsigned int num;
> > +    const char *p;
> > +
> 
> Perhaps a little comment:
> 
>   /* terminating with 0/1 to disable/enable all */
> 
> > +    catch_syscalls_none = strcmp(param, "0") == 0;
> > +    if (catch_syscalls_none || strcmp(param, "1") == 0) {
> > +    gdbserver_user_state.catch_syscalls_state =
> > +    catch_syscalls_none ? GDB_CATCH_SYSCALLS_NONE :
> > +  GDB_CATCH_SYSCALLS_ALL;
> > +    gdb_put_packet("OK");
> > +    return;
> > +    }
> 
>   /* otherwise decode the following list of syscalls... */
> 
> ?

Ok.

> 
> > +    if (param[0] == '1' && param[1] == ';') {
> > +    catch_syscalls_state = GDB_CATCH_SYSCALLS_SELECTED;
> > +    memset(catch_syscalls_mask, 0,
> > sizeof(catch_syscalls_mask));
> > +    for (p = [2];; p++) {
> > +    if (qemu_strtoui(p, , 16, ) || (*p && *p !=
> > ';')) {
> > +    goto err;
> > +    }
> > +    if (num >= GDB_NR_SYSCALLS) {
> > +    /* Fall back to reporting all syscalls. */
> > +    catch_syscalls_state = GDB_CATCH_SYSCALLS_ALL;
> 
> Is this the

[PATCH v4 0/4] target/s390x: Emulate CVDG and CVB*

2024-02-02 Thread Ilya Leoshkevich
v3: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg06664.html
v3 -> v4: Implement CVB error handling (David/Thomas).

v2: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg05048.html
v2 -> v3: Resurrect an old CVB* patch (Thomas).
  Add Richard's R-b.

v1: https://lists.gnu.org/archive/html/qemu-devel/2024-01/msg02865.html
v1 -> v2: Fix !CONFIG_INT128 builds (Richard).

Hi,

Ido reported that we are missing the CVDG emulation (which is very
similar to the existing CVD emulation). This series adds it along with
a test.

Best regards,
Ilya

Ilya Leoshkevich (4):
  target/s390x: Emulate CVDG
  target/s390x: Emulate CVB, CVBY and CVBG
  tests/tcg/s390x: Test CONVERT TO DECIMAL
  tests/tcg/s390x: Test CONVERT TO BINARY

 target/s390x/helper.h|   3 +
 target/s390x/tcg/insn-data.h.inc |   5 ++
 target/s390x/tcg/int_helper.c|  93 
 target/s390x/tcg/translate.c |  24 
 tests/tcg/s390x/Makefile.target  |   2 +
 tests/tcg/s390x/cvb.c| 102 +++
 tests/tcg/s390x/cvd.c|  63 +++
 7 files changed, 292 insertions(+)
 create mode 100644 tests/tcg/s390x/cvb.c
 create mode 100644 tests/tcg/s390x/cvd.c

-- 
2.43.0




[PATCH v4 1/4] target/s390x: Emulate CVDG

2024-02-02 Thread Ilya Leoshkevich
CVDG is the same as CVD, except that it converts 64 bits into 128,
rather than 32 into 64. Create a new helper, which uses Int128
wrappers.

Reported-by: Ido Plat 
Reviewed-by: Richard Henderson 
Signed-off-by: Ilya Leoshkevich 
---
 target/s390x/helper.h|  1 +
 target/s390x/tcg/insn-data.h.inc |  1 +
 target/s390x/tcg/int_helper.c| 21 +
 target/s390x/tcg/translate.c |  8 
 4 files changed, 31 insertions(+)

diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 05102578fc9..332a9a9c632 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -89,6 +89,7 @@ DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqxb, TCG_CALL_NO_WG, i128, env, i128)
 DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
+DEF_HELPER_FLAGS_1(cvdg, TCG_CALL_NO_RWG_SE, i128, s64)
 DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_WG, void, env, i32, i64, i64)
 DEF_HELPER_FLAGS_4(pka, TCG_CALL_NO_WG, void, env, i64, i64, i32)
 DEF_HELPER_FLAGS_4(pku, TCG_CALL_NO_WG, void, env, i64, i64, i32)
diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc
index 2f07f39d9cb..388dcb8dbbc 100644
--- a/target/s390x/tcg/insn-data.h.inc
+++ b/target/s390x/tcg/insn-data.h.inc
@@ -296,6 +296,7 @@
 /* CONVERT TO DECIMAL */
 C(0x4e00, CVD, RX_a,  Z,   r1_o, a2, 0, 0, cvd, 0)
 C(0xe326, CVDY,RXY_a, LD,  r1_o, a2, 0, 0, cvd, 0)
+C(0xe32e, CVDG,RXY_a, Z,   r1_o, a2, 0, 0, cvdg, 0)
 /* CONVERT TO FIXED */
 F(0xb398, CFEBR,   RRF_e, Z,   0, e2, new, r1_32, cfeb, 0, IF_BFP)
 F(0xb399, CFDBR,   RRF_e, Z,   0, f2, new, r1_32, cfdb, 0, IF_BFP)
diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c
index eb8e6dd1b57..121e3006a65 100644
--- a/target/s390x/tcg/int_helper.c
+++ b/target/s390x/tcg/int_helper.c
@@ -118,6 +118,27 @@ uint64_t HELPER(cvd)(int32_t reg)
 return dec;
 }
 
+Int128 HELPER(cvdg)(int64_t reg)
+{
+/* positive 0 */
+Int128 dec = int128_make64(0x0c);
+Int128 bin = int128_makes64(reg);
+Int128 base = int128_make64(10);
+int shift;
+
+if (!int128_nonneg(bin)) {
+bin = int128_neg(bin);
+dec = int128_make64(0x0d);
+}
+
+for (shift = 4; (shift < 128) && int128_nz(bin); shift += 4) {
+dec = int128_or(dec, int128_lshift(int128_remu(bin, base), shift));
+bin = int128_divu(bin, base);
+}
+
+return dec;
+}
+
 uint64_t HELPER(popcnt)(uint64_t val)
 {
 /* Note that we don't fold past bytes. */
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index a5fd9cccaa5..c2fdc920a50 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -2233,6 +2233,14 @@ static DisasJumpType op_cvd(DisasContext *s, DisasOps *o)
 return DISAS_NEXT;
 }
 
+static DisasJumpType op_cvdg(DisasContext *s, DisasOps *o)
+{
+TCGv_i128 t = tcg_temp_new_i128();
+gen_helper_cvdg(t, o->in1);
+tcg_gen_qemu_st_i128(t, o->in2, get_mem_index(s), MO_TE | MO_128);
+return DISAS_NEXT;
+}
+
 static DisasJumpType op_ct(DisasContext *s, DisasOps *o)
 {
 int m3 = get_field(s, m3);
-- 
2.43.0




[PATCH v4 3/4] tests/tcg/s390x: Test CONVERT TO DECIMAL

2024-02-02 Thread Ilya Leoshkevich
Check the CVD's, CVDY's, and CVDG's corner cases.

Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/s390x/Makefile.target |  1 +
 tests/tcg/s390x/cvd.c   | 63 +
 2 files changed, 64 insertions(+)
 create mode 100644 tests/tcg/s390x/cvd.c

diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 30994dcf9c2..04e4bddd83d 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -45,6 +45,7 @@ TESTS+=clc
 TESTS+=laalg
 TESTS+=add-logical-with-carry
 TESTS+=lae
+TESTS+=cvd
 
 cdsg: CFLAGS+=-pthread
 cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/cvd.c b/tests/tcg/s390x/cvd.c
new file mode 100644
index 000..d776688985e
--- /dev/null
+++ b/tests/tcg/s390x/cvd.c
@@ -0,0 +1,63 @@
+/*
+ * Test the CONVERT TO DECIMAL instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+
+static uint64_t cvd(int32_t x)
+{
+uint64_t ret;
+
+asm("cvd %[x],%[ret]" : [ret] "=R" (ret) : [x] "r" (x));
+
+return ret;
+}
+
+static uint64_t cvdy(int32_t x)
+{
+uint64_t ret;
+
+asm("cvdy %[x],%[ret]" : [ret] "=T" (ret) : [x] "r" (x));
+
+return ret;
+}
+
+static __uint128_t cvdg(int64_t x)
+{
+__uint128_t ret;
+
+asm("cvdg %[x],%[ret]" : [ret] "=T" (ret) : [x] "r" (x));
+
+return ret;
+}
+
+int main(void)
+{
+__uint128_t m = (((__uint128_t)0x9223372036854775) << 16) | 0x8070;
+
+assert(cvd(0) == 0xc);
+assert(cvd(1) == 0x1c);
+assert(cvd(25594) == 0x25594c);
+assert(cvd(-1) == 0x1d);
+assert(cvd(0x7fff) == 0x2147483647c);
+assert(cvd(-0x8000) == 0x2147483648d);
+
+assert(cvdy(0) == 0xc);
+assert(cvdy(1) == 0x1c);
+assert(cvdy(25594) == 0x25594c);
+assert(cvdy(-1) == 0x1d);
+assert(cvdy(0x7fff) == 0x2147483647c);
+assert(cvdy(-0x8000) == 0x2147483648d);
+
+assert(cvdg(0) == 0xc);
+assert(cvdg(1) == 0x1c);
+assert(cvdg(25594) == 0x25594c);
+assert(cvdg(-1) == 0x1d);
+assert(cvdg(0x7fff) == (m + 0xc));
+assert(cvdg(-0x8000) == (m + 0x1d));
+
+return EXIT_SUCCESS;
+}
-- 
2.43.0




[PATCH v4 4/4] tests/tcg/s390x: Test CONVERT TO BINARY

2024-02-02 Thread Ilya Leoshkevich
Check the CVB's, CVBY's, and CVBG's corner cases.

Co-developed-by: Pavel Zbitskiy 
Signed-off-by: Ilya Leoshkevich 
---
 tests/tcg/s390x/Makefile.target |   1 +
 tests/tcg/s390x/cvb.c   | 102 
 2 files changed, 103 insertions(+)
 create mode 100644 tests/tcg/s390x/cvb.c

diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 04e4bddd83d..e2aba2ec274 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -46,6 +46,7 @@ TESTS+=laalg
 TESTS+=add-logical-with-carry
 TESTS+=lae
 TESTS+=cvd
+TESTS+=cvb
 
 cdsg: CFLAGS+=-pthread
 cdsg: LDFLAGS+=-pthread
diff --git a/tests/tcg/s390x/cvb.c b/tests/tcg/s390x/cvb.c
new file mode 100644
index 000..e1735f6b81c
--- /dev/null
+++ b/tests/tcg/s390x/cvb.c
@@ -0,0 +1,102 @@
+/*
+ * Test the CONVERT TO BINARY instruction.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int signum;
+
+static void signal_handler(int n)
+{
+signum = n;
+}
+
+#define FAIL 0x1234567887654321
+#define OK32(x) (0x12345678 | (uint32_t)(x))
+
+static int64_t cvb(uint64_t x)
+{
+int64_t ret = FAIL;
+
+signum = -1;
+asm("cvb %[ret],%[x]" : [ret] "+r" (ret) : [x] "R" (x));
+
+return ret;
+}
+
+static int64_t cvby(uint64_t x)
+{
+int64_t ret = FAIL;
+
+signum = -1;
+asm("cvby %[ret],%[x]" : [ret] "+r" (ret) : [x] "T" (x));
+
+return ret;
+}
+
+static int64_t cvbg(__uint128_t x)
+{
+int64_t ret = FAIL;
+
+signum = -1;
+asm("cvbg %[ret],%[x]" : [ret] "+r" (ret) : [x] "T" (x));
+
+return ret;
+}
+
+int main(void)
+{
+__uint128_t m = (((__uint128_t)0x9223372036854775) << 16) | 0x8070;
+struct sigaction act;
+int err;
+
+memset(, 0, sizeof(act));
+act.sa_handler = signal_handler;
+err = sigaction(SIGFPE, , NULL);
+assert(err == 0);
+err = sigaction(SIGILL, , NULL);
+assert(err == 0);
+
+assert(cvb(0xc) == OK32(0) && signum == -1);
+assert(cvb(0x1c) == OK32(1) && signum == -1);
+assert(cvb(0x25594c) == OK32(25594) && signum == -1);
+assert(cvb(0x1d) == OK32(-1) && signum == -1);
+assert(cvb(0x2147483647c) == OK32(0x7fff) && signum == -1);
+assert(cvb(0x2147483648d) == OK32(-0x8000) && signum == -1);
+assert(cvb(0x7) == FAIL && signum == SIGILL);
+assert(cvb(0x2147483648c) == OK32(0x8000) && signum == SIGFPE);
+assert(cvb(0x30c) == OK32(0xb2d05e00) && signum == SIGFPE);
+assert(cvb(0x2147483649d) == OK32(0x7fff) && signum == SIGFPE);
+assert(cvb(0x30d) == OK32(0x4d2fa200) && signum == SIGFPE);
+
+assert(cvby(0xc) == OK32(0));
+assert(cvby(0x1c) == OK32(1));
+assert(cvby(0x25594c) == OK32(25594));
+assert(cvby(0x1d) == OK32(-1));
+assert(cvby(0x2147483647c) == OK32(0x7fff));
+assert(cvby(0x2147483648d) == OK32(-0x8000));
+assert(cvby(0x7) == FAIL && signum == SIGILL);
+assert(cvby(0x2147483648c) == OK32(0x8000) && signum == SIGFPE);
+assert(cvby(0x30c) == OK32(0xb2d05e00) && signum == SIGFPE);
+assert(cvby(0x2147483649d) == OK32(0x7fff) && signum == SIGFPE);
+assert(cvby(0x30d) == OK32(0x4d2fa200) && signum == SIGFPE);
+
+assert(cvbg(0xc) == 0);
+assert(cvbg(0x1c) == 1);
+assert(cvbg(0x25594c) == 25594);
+assert(cvbg(0x1d) == -1);
+assert(cvbg(m + 0xc) == 0x7fff);
+assert(cvbg(m + 0x1d) == -0x8000);
+assert(cvbg(0x7) == FAIL && signum == SIGILL);
+assert(cvbg(m + 0x1c) == FAIL && signum == SIGFPE);
+assert(cvbg(m + 0x2d) == FAIL && signum == SIGFPE);
+assert(cvbg(((__uint128_t)1 << 80) + 0xc) == FAIL && signum == SIGFPE);
+assert(cvbg(((__uint128_t)1 << 80) + 0xd) == FAIL && signum == SIGFPE);
+
+return EXIT_SUCCESS;
+}
-- 
2.43.0




[PATCH v4 2/4] target/s390x: Emulate CVB, CVBY and CVBG

2024-02-02 Thread Ilya Leoshkevich
Convert to Binary - counterparts of the already implemented Convert
to Decimal (CVD*) instructions.
Example from the Principles of Operation: 25594C becomes 63FA.

Co-developed-by: Pavel Zbitskiy 
Signed-off-by: Ilya Leoshkevich 
---
 target/s390x/helper.h|  2 +
 target/s390x/tcg/insn-data.h.inc |  4 ++
 target/s390x/tcg/int_helper.c| 72 
 target/s390x/tcg/translate.c | 16 +++
 4 files changed, 94 insertions(+)

diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 332a9a9c632..cc1c20e9e3f 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -88,6 +88,8 @@ DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_NO_RWG_SE, i32, env, i128, 
i64)
 DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqxb, TCG_CALL_NO_WG, i128, env, i128)
+DEF_HELPER_3(cvb, void, env, i32, i64)
+DEF_HELPER_FLAGS_2(cvbg, TCG_CALL_NO_WG, i64, env, i128)
 DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
 DEF_HELPER_FLAGS_1(cvdg, TCG_CALL_NO_RWG_SE, i128, s64)
 DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_WG, void, env, i32, i64, i64)
diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc
index 388dcb8dbbc..e7d61cdec28 100644
--- a/target/s390x/tcg/insn-data.h.inc
+++ b/target/s390x/tcg/insn-data.h.inc
@@ -293,6 +293,10 @@
 D(0xec73, CLFIT,   RIE_a, GIE, r1_32u, i2_16u, 0, 0, ct, 0, 1)
 D(0xec71, CLGIT,   RIE_a, GIE, r1_o, i2_16u, 0, 0, ct, 0, 1)
 
+/* CONVERT TO BINARY */
+C(0x4f00, CVB, RX_a,  Z,   la2, 0, 0, 0, cvb, 0)
+C(0xe306, CVBY,RXY_a, LD,  la2, 0, 0, 0, cvb, 0)
+C(0xe30e, CVBG,RXY_a, Z,   la2, 0, r1, 0, cvbg, 0)
 /* CONVERT TO DECIMAL */
 C(0x4e00, CVD, RX_a,  Z,   r1_o, a2, 0, 0, cvd, 0)
 C(0xe326, CVDY,RXY_a, LD,  r1_o, a2, 0, 0, cvd, 0)
diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c
index 121e3006a65..17974375e98 100644
--- a/target/s390x/tcg/int_helper.c
+++ b/target/s390x/tcg/int_helper.c
@@ -25,6 +25,7 @@
 #include "exec/exec-all.h"
 #include "qemu/host-utils.h"
 #include "exec/helper-proto.h"
+#include "exec/cpu_ldst.h"
 
 /* #define DEBUG_HELPER */
 #ifdef DEBUG_HELPER
@@ -98,6 +99,77 @@ Int128 HELPER(divu64)(CPUS390XState *env, uint64_t ah, 
uint64_t al, uint64_t b)
 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
 }
 
+void HELPER(cvb)(CPUS390XState *env, uint32_t r1, uint64_t dec)
+{
+int64_t pow10 = 1, bin = 0;
+int digit, sign;
+
+sign = dec & 0xf;
+if (sign < 0xa) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec >>= 4;
+
+while (dec) {
+digit = dec & 0xf;
+if (digit > 0x9) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec >>= 4;
+bin += digit * pow10;
+pow10 *= 10;
+}
+
+if (sign == 0xb || sign == 0xd) {
+bin = -bin;
+}
+
+/* R1 is updated even on fixed-point-divide exception. */
+env->regs[r1] = (env->regs[r1] & 0xULL) | (uint32_t)bin;
+if (bin != (int32_t)bin) {
+tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
+}
+}
+
+uint64_t HELPER(cvbg)(CPUS390XState *env, Int128 dec)
+{
+uint64_t dec64[] = {int128_getlo(dec), int128_gethi(dec)};
+int64_t bin = 0, pow10, tmp;
+int digit, i, sign;
+
+sign = dec64[0] & 0xf;
+if (sign < 0xa) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec64[0] >>= 4;
+pow10 = (sign == 0xb || sign == 0xd) ? -1 : 1;
+
+for (i = 1; i < 20; i++) {
+digit = dec64[i >> 4] & 0xf;
+if (digit > 0x9) {
+tcg_s390_data_exception(env, 0, GETPC());
+}
+dec64[i >> 4] >>= 4;
+tmp = pow10 * digit;
+if (digit && ((tmp ^ pow10) < 0)) {
+tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
+}
+tmp = bin + tmp;
+if (bin && ((tmp ^ bin) < 0)) {
+tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
+}
+bin = tmp;
+pow10 *= 10;
+}
+
+g_assert(!dec64[0]);
+if (dec64[1]) {
+tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
+}
+
+return bin;
+}
+
 uint64_t HELPER(cvd)(int32_t reg)
 {
 /* positive 0 */
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index c2fdc920a50..325b25959d3 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -2223,6 +2223,22 @@ static DisasJumpType op_csp(DisasContext *s, DisasOps *o)
 }
 #endif
 
+static DisasJumpType op_cvb(DisasContext *s, DisasOps *o)
+{
+TCGv_i64 t = tcg_temp_new_i64();
+tcg_gen_qemu_ld_i64(t, o->addr1, get_mem_index(s), MO_TEUQ);
+gen_helper_cvb(tcg_env, tcg_constant_i32(get_field(s, r1)), t);
+return DISAS_NE

Re: [PATCH 2/3] gdbstub: Implement follow-fork-mode child

2024-02-01 Thread Ilya Leoshkevich
On Thu, 2024-02-01 at 12:11 +, Alex Bennée wrote:
> Ilya Leoshkevich  writes:
> 
> > Currently it's not possible to use gdbstub for debugging linux-user
> > code that runs in a forked child, which is normally done using the
> > `set
> > follow-fork-mode child` GDB command. Purely on the protocol level,
> > the
> > missing piece is the fork-events feature.
> > 
> > However, a deeper problem is supporting $Hg switching between
> > different
> > processes - right now it can do only threads. Implementing this for
> > the
> > general case would be quite complicated, but, fortunately, for the
> > follow-fork-mode case there are a few factors that greatly simplify
> > things: fork() happens in the exclusive section, there are only two
> > processes involved, and before one of them is resumed, the second
> > one
> > is detached.
> > 
> > This makes it possible to implement a simplified scheme: the parent
> > and
> > the child share the gdbserver socket, it's used only by one of them
> > at
> > any given time, which is coordinated through a separate socketpair.
> > The
> > processes can read from the gdbserver socket only one byte at a
> > time,
> > which is not great for performance, but, fortunately, the
> > follow-fork-mode involves only a few messages.
> > 
> > Add the hooks for the user-specific handling of $qSupported, $Hg,
> > and
> > $D. Advertise the fork-events support, and remember whether GDB has
> > it
> > as well. Implement the state machine that is initialized on fork(),
> > decides the current owner of the gdbserver socket, and is
> > terminated
> > when one of the two processes is detached. The logic for the parent
> > and
> > the child is the same, only the initial state is different.
> > 
> > Handle the `stepi` of a syscall corner case by disabling the
> > single-stepping in detached processes.
> > 
> > Signed-off-by: Ilya Leoshkevich 
> > ---
> >  gdbstub/gdbstub.c   |  29 --
> >  gdbstub/internals.h |   3 +
> >  gdbstub/user.c  | 210
> > +++-
> >  3 files changed, 234 insertions(+), 8 deletions(-)

[...]
 
> > diff --git a/gdbstub/user.c b/gdbstub/user.c
> > index 120eb7fc117..962f4cb74e7 100644
> > --- a/gdbstub/user.c
> > +++ b/gdbstub/user.c
> > @@ -10,6 +10,7 @@
> >   */
> >  
> >  #include "qemu/osdep.h"
> > +#include 
> >  #include "qemu/bitops.h"
> >  #include "qemu/cutils.h"
> >  #include "qemu/sockets.h"
> > @@ -25,6 +26,41 @@
> >  #define GDB_NR_SYSCALLS 1024
> >  typedef unsigned long
> > GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)];
> >  
> > +/*
> > + * Forked child talks to its parent in order to let GDB enforce
> > the
> > + * follow-fork-mode. This happens inside a start_exclusive()
> > section, so that
> > + * the other threads, which may be forking too, do not interfere.
> > The
> > + * implementation relies on GDB not sending $vCont until it has
> > detached
> > + * either from the parent (follow-fork-mode child) or from the
> > child
> > + * (follow-fork-mode parent).
> > + *
> > + * The parent and the child share the GDB socket; at any given
> > time only one
> > + * of them is allowed to use it, as is reflected in the respective
> > fork_state.
> > + * This is negotiated via the fork_sockets pair as a reaction to
> > $Hg.
> > + */
> > +enum GDBForkState {
> > +    /* Fully owning the GDB socket. */
> > +    GDB_FORK_ENABLED,
> > +    /* Working with the GDB socket; the peer is inactive. */
> > +    GDB_FORK_ACTIVE,
> > +    /* Handing off the GDB socket to the peer. */
> > +    GDB_FORK_DEACTIVATING,
> > +    /* The peer is working with the GDB socket. */
> > +    GDB_FORK_INACTIVE,
> > +    /* Asking the peer to close its GDB socket fd. */
> > +    GDB_FORK_ENABLING,
> > +    /* Asking the peer to take over, closing our GDB socket fd. */
> > +    GDB_FORK_DISABLING,
> > +    /* The peer has taken over, our GDB socket fd is closed. */
> > +    GDB_FORK_DISABLED,
> > +};
> 
> gulp - thats a potentially fairly complex state diagram. Do we just
> work
> through the states sequentially?

Unfortunately no. I had less states at some point, but then realized
it was better to have these things laid out explicitly. Let me try to
summarize the possible transitions:

GDB_FORK_ENABLED: Terminal state; GDB follows the current process.
GDB_FORK_DISABLED: Te

[PATCH v3 3/4] target/s390x: implement CVB, CVBY and CVBG

2024-01-31 Thread Ilya Leoshkevich
From: Pavel Zbitskiy 

Convert to Binary - counterparts of the already implemented Convert
to Decimal (CVD*) instructions.
Example from the Principles of Operation: 25594C becomes 63FA.

[iii: Use separate functions for CVB and CVBG for simplicity].

Signed-off-by: Pavel Zbitskiy 
---
 target/s390x/helper.h|  1 +
 target/s390x/tcg/insn-data.h.inc |  4 
 target/s390x/tcg/int_helper.c| 40 
 target/s390x/tcg/translate.c | 12 ++
 4 files changed, 57 insertions(+)

diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 332a9a9c632..3c607f4e437 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -88,6 +88,7 @@ DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_NO_RWG_SE, i32, env, i128, 
i64)
 DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64)
 DEF_HELPER_FLAGS_2(sqxb, TCG_CALL_NO_WG, i128, env, i128)
+DEF_HELPER_FLAGS_3(cvb, TCG_CALL_NO_WG, i64, env, i64, i32)
 DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
 DEF_HELPER_FLAGS_1(cvdg, TCG_CALL_NO_RWG_SE, i128, s64)
 DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_WG, void, env, i32, i64, i64)
diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc
index 388dcb8dbbc..9eb998d4c25 100644
--- a/target/s390x/tcg/insn-data.h.inc
+++ b/target/s390x/tcg/insn-data.h.inc
@@ -293,6 +293,10 @@
 D(0xec73, CLFIT,   RIE_a, GIE, r1_32u, i2_16u, 0, 0, ct, 0, 1)
 D(0xec71, CLGIT,   RIE_a, GIE, r1_o, i2_16u, 0, 0, ct, 0, 1)
 
+/* CONVERT TO BINARY */
+C(0x4f00, CVB, RX_a,  Z,   la2, 0, new, r1_32, cvb, 0)
+C(0xe306, CVBY,RXY_a, LD,  la2, 0, new, r1_32, cvb, 0)
+C(0xe30e, CVBG,RXY_a, Z,   la2, 0, r1, 0, cvbg, 0)
 /* CONVERT TO DECIMAL */
 C(0x4e00, CVD, RX_a,  Z,   r1_o, a2, 0, 0, cvd, 0)
 C(0xe326, CVDY,RXY_a, LD,  r1_o, a2, 0, 0, cvd, 0)
diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c
index 121e3006a65..002d4b52dda 100644
--- a/target/s390x/tcg/int_helper.c
+++ b/target/s390x/tcg/int_helper.c
@@ -25,6 +25,7 @@
 #include "exec/exec-all.h"
 #include "qemu/host-utils.h"
 #include "exec/helper-proto.h"
+#include "exec/cpu_ldst.h"
 
 /* #define DEBUG_HELPER */
 #ifdef DEBUG_HELPER
@@ -98,6 +99,45 @@ Int128 HELPER(divu64)(CPUS390XState *env, uint64_t ah, 
uint64_t al, uint64_t b)
 tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC());
 }
 
+uint64_t HELPER(cvb)(CPUS390XState *env, uint64_t src, uint32_t n)
+{
+int64_t dec, sign = 0, digit, val = 0, pow10 = 0;
+const uintptr_t ra = GETPC();
+uint64_t tmpsrc;
+int i, j;
+
+for (i = 0; i < n; i++) {
+tmpsrc = wrap_address(env, src + (n - i - 1) * 8);
+dec = cpu_ldq_data_ra(env, tmpsrc, ra);
+for (j = 0; j < 16; j++, dec >>= 4) {
+if (i == 0 && j == 0) {
+sign = dec & 0xf;
+if (sign < 0xa) {
+tcg_s390_data_exception(env, 0, ra);
+}
+continue;
+}
+digit = dec & 0xf;
+if (digit > 0x9) {
+tcg_s390_data_exception(env, 0, ra);
+}
+if (i == 0 && j == 1) {
+if (sign == 0xb || sign == 0xd) {
+val = -digit;
+pow10 = -10;
+} else {
+val = digit;
+pow10 = 10;
+}
+} else {
+val += digit * pow10;
+pow10 *= 10;
+}
+}
+}
+return val;
+}
+
 uint64_t HELPER(cvd)(int32_t reg)
 {
 /* positive 0 */
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index c2fdc920a50..43216571b44 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -2223,6 +2223,18 @@ static DisasJumpType op_csp(DisasContext *s, DisasOps *o)
 }
 #endif
 
+static DisasJumpType op_cvb(DisasContext *s, DisasOps *o)
+{
+gen_helper_cvb(o->out, tcg_env, o->addr1, tcg_constant_i32(1));
+return DISAS_NEXT;
+}
+
+static DisasJumpType op_cvbg(DisasContext *s, DisasOps *o)
+{
+gen_helper_cvb(o->out, tcg_env, o->addr1, tcg_constant_i32(2));
+return DISAS_NEXT;
+}
+
 static DisasJumpType op_cvd(DisasContext *s, DisasOps *o)
 {
 TCGv_i64 t1 = tcg_temp_new_i64();
-- 
2.43.0




  1   2   3   4   5   6   7   8   9   10   >