summary refs log tree commit diff stats
path: root/linux-user
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2025-09-24 12:02:13 -0700
committerRichard Henderson <richard.henderson@linaro.org>2025-09-24 12:02:13 -0700
commit48d7b47cd76b986ad360b6ba1b0889186416f1c2 (patch)
tree6cd0f2f0a38de0a8cb2e968243c5e3e3b1565d10 /linux-user
parentab8008b231e758e03c87c1c483c03afdd9c02e19 (diff)
parentf6f7fdd68e6fbfafae828e504de544b5659bc4bd (diff)
downloadfocaccia-qemu-48d7b47cd76b986ad360b6ba1b0889186416f1c2.tar.gz
focaccia-qemu-48d7b47cd76b986ad360b6ba1b0889186416f1c2.zip
Merge tag 'pull-misc-20250924' of https://gitlab.com/rth7680/qemu into staging
hw/pci-host/{dino,astro}: Don't call pci_register_root_bus() in init
target/sparc: Loosen various decode for v7
linux-user: Add syscall dispatch support
tcg/optimize: Fix folding of vector bitsel
include/hw/core/cpu: Introduce MMUIdxMap
include/hw/core/cpu: Introduce cpu_tlb_fast
include/hw/core/cpu: Invert the indexing into CPUTLBDescFast
accel/tcg: Remove dead mmap_unlock() call in invalidate_phys_page_range
accel/tcg: Remove cpu_loop_exit_restore() stub
accel/tcg: Properly unlink a TB linked to itself
accel/tcg: Introduce and use tb_flush__exclusive_or_serial

# -----BEGIN PGP SIGNATURE-----
#
# iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmjUP5MdHHJpY2hhcmQu
# aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV/qEwgAt6uSXMVTXykr6uxW
# 321nMEMEB2Av5LHQwvgRW/BOAWCKDNtxHHn3tcfvOLKcFHR+agZqTHBvOKGgPGSo
# fPkoHRMlcb3pKxhttX66qZhDiaMNRALtajVNkelKUso4BtESkW1v4yQVNLr1Rk6+
# f/xg4noX2gSh56VDMGLgcTR5wvTNycTIq3909zPmO4YPVQjwUPSYkB227LyBRLYg
# R6EQOzn45oQuFfMYukjNQczibkZ7NV8mW7XmbfiMXwvK1yA/F75eN+B9sJKqS44d
# ww/rurQdIYZFwPPPqz3XZmztg0n9syE9VHkliYmAoJRtbgc0obQHt9M7UfLwW2TM
# NXYlNw==
# =HVcw
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 24 Sep 2025 11:59:31 AM PDT
# gpg:                using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg:                issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [ultimate]

* tag 'pull-misc-20250924' of https://gitlab.com/rth7680/qemu: (32 commits)
  accel/tcg: Remove cpu_loop_exit_restore() stub
  accel/tcg: Remove dead mmap_unlock() call in invalidate_phys_page_range
  accel/tcg: Improve buffer overflow in tb_gen_code
  accel/tcg: Create queue_tb_flush from tb_flush
  linux-user: Split out begin_parallel_context
  plugins: Use tb_flush__exclusive_or_serial
  accel/tcg: Move post-load tb_flush to vm_change_state hook
  accel/tcg: Split out tb_flush__exclusive_or_serial
  hw/ppc/spapr: Use tb_invalidate_phys_range in h_page_init
  target/riscv: Record misa_ext in TCGTBCPUState.cs_base
  target/alpha: Simplify call_pal implementation
  gdbstub: Remove tb_flush uses
  tests/tcg/multiarch: Add tb-link test
  accel/tcg: Properly unlink a TB linked to itself
  target/hppa: Adjust mmu indexes to begin with 0
  include/hw/core/cpu: Invert the indexing into CPUTLBDescFast
  include/hw/core/cpu: Introduce cpu_tlb_fast
  include/hw/core/cpu: Introduce MMUIdxMap
  tcg/optimize: Fix folding of vector bitsel
  hw/pci-host/astro: Don't call pci_regsiter_root_bus() in init
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'linux-user')
-rwxr-xr-xlinux-user/aarch64/vdso-be.sobin3224 -> 3320 bytes
-rwxr-xr-xlinux-user/aarch64/vdso-le.sobin3224 -> 3320 bytes
-rw-r--r--linux-user/aarch64/vdso.S2
-rw-r--r--linux-user/alpha/cpu_loop.c5
-rwxr-xr-xlinux-user/arm/vdso-be32.sobin2648 -> 2724 bytes
-rwxr-xr-xlinux-user/arm/vdso-be8.sobin2648 -> 2724 bytes
-rwxr-xr-xlinux-user/arm/vdso-le.sobin2648 -> 2724 bytes
-rw-r--r--linux-user/arm/vdso.S2
-rw-r--r--linux-user/elfload.c7
-rw-r--r--linux-user/gen-vdso-elfn.c.inc7
-rw-r--r--linux-user/gen-vdso.c6
-rw-r--r--linux-user/hppa/vdso.S2
-rwxr-xr-xlinux-user/hppa/vdso.sobin2104 -> 2224 bytes
-rw-r--r--linux-user/i386/vdso.S2
-rwxr-xr-xlinux-user/i386/vdso.sobin2672 -> 2792 bytes
-rw-r--r--linux-user/loader.h2
-rw-r--r--linux-user/loongarch64/vdso.S2
-rwxr-xr-xlinux-user/loongarch64/vdso.sobin3560 -> 3712 bytes
-rw-r--r--linux-user/main.c2
-rw-r--r--linux-user/mmap.c13
-rwxr-xr-xlinux-user/ppc/vdso-32.sobin3020 -> 3140 bytes
-rwxr-xr-xlinux-user/ppc/vdso-64.sobin3896 -> 4048 bytes
-rwxr-xr-xlinux-user/ppc/vdso-64le.sobin3896 -> 4048 bytes
-rw-r--r--linux-user/ppc/vdso.S2
-rw-r--r--linux-user/qemu.h5
-rwxr-xr-xlinux-user/riscv/vdso-32.sobin2980 -> 3124 bytes
-rwxr-xr-xlinux-user/riscv/vdso-64.sobin3944 -> 4104 bytes
-rw-r--r--linux-user/riscv/vdso.S2
-rw-r--r--linux-user/s390x/vdso.S2
-rwxr-xr-xlinux-user/s390x/vdso.sobin3464 -> 3616 bytes
-rw-r--r--linux-user/signal-common.h7
-rw-r--r--linux-user/signal.c2
-rw-r--r--linux-user/syscall.c83
-rw-r--r--linux-user/syscall_defs.h6
-rw-r--r--linux-user/user-internals.h16
35 files changed, 152 insertions, 25 deletions
diff --git a/linux-user/aarch64/vdso-be.so b/linux-user/aarch64/vdso-be.so
index d43c3b19cd..4089838b30 100755
--- a/linux-user/aarch64/vdso-be.so
+++ b/linux-user/aarch64/vdso-be.so
Binary files differdiff --git a/linux-user/aarch64/vdso-le.so b/linux-user/aarch64/vdso-le.so
index aaedc9d85e..240802821c 100755
--- a/linux-user/aarch64/vdso-le.so
+++ b/linux-user/aarch64/vdso-le.so
Binary files differdiff --git a/linux-user/aarch64/vdso.S b/linux-user/aarch64/vdso.S
index a0ac1487b0..59dd94dc8f 100644
--- a/linux-user/aarch64/vdso.S
+++ b/linux-user/aarch64/vdso.S
@@ -71,5 +71,7 @@ vdso_syscall __kernel_clock_getres, __NR_clock_getres
 __kernel_rt_sigreturn:
 	/* No BTI C insn here -- we arrive via RET. */
 	mov	x8, #__NR_rt_sigreturn
+sigreturn_region_start:
 	svc	#0
+sigreturn_region_end:
 endf	__kernel_rt_sigreturn
diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c
index bb8346b509..f93597c400 100644
--- a/linux-user/alpha/cpu_loop.c
+++ b/linux-user/alpha/cpu_loop.c
@@ -94,11 +94,6 @@ void cpu_loop(CPUAlphaState *env)
                 break;
             case 0x86:
                 /* IMB */
-                /* ??? We can probably elide the code using page_unprotect
-                   that is checking for self-modifying code.  Instead we
-                   could simply call tb_flush here.  Until we work out the
-                   changes required to turn off the extra write protection,
-                   this can be a no-op.  */
                 break;
             case 0x9E:
                 /* RDUNIQUE */
diff --git a/linux-user/arm/vdso-be32.so b/linux-user/arm/vdso-be32.so
index b896d3d545..6d71cd9c36 100755
--- a/linux-user/arm/vdso-be32.so
+++ b/linux-user/arm/vdso-be32.so
Binary files differdiff --git a/linux-user/arm/vdso-be8.so b/linux-user/arm/vdso-be8.so
index 784b7bdb2a..6446a96fcf 100755
--- a/linux-user/arm/vdso-be8.so
+++ b/linux-user/arm/vdso-be8.so
Binary files differdiff --git a/linux-user/arm/vdso-le.so b/linux-user/arm/vdso-le.so
index 38d3d51047..d34e577b69 100755
--- a/linux-user/arm/vdso-le.so
+++ b/linux-user/arm/vdso-le.so
Binary files differdiff --git a/linux-user/arm/vdso.S b/linux-user/arm/vdso.S
index b3bb6491dc..d84d964730 100644
--- a/linux-user/arm/vdso.S
+++ b/linux-user/arm/vdso.S
@@ -140,6 +140,7 @@ SYSCALL __vdso_gettimeofday, __NR_gettimeofday
 
 	.balign	16
 sigreturn_codes:
+sigreturn_region_start:
 	/* [EO]ABI sigreturn */
 	slot	0
 	raw_syscall __NR_sigreturn
@@ -172,3 +173,4 @@ sigreturn_codes:
 
 	.balign	16
 endf sigreturn_codes
+sigreturn_region_end:
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 26c090c95d..1370ec59be 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1659,6 +1659,11 @@ static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso)
     if (vdso->rt_sigreturn_ofs) {
         default_rt_sigreturn = load_addr + vdso->rt_sigreturn_ofs;
     }
+    if (vdso->sigreturn_region_start_ofs) {
+        vdso_sigreturn_region_start =
+            load_addr + vdso->sigreturn_region_start_ofs;
+        vdso_sigreturn_region_end = load_addr + vdso->sigreturn_region_end_ofs;
+    }
 
     /* Remove write from VDSO segment. */
     target_mprotect(info->start_data, info->end_data - info->start_data,
@@ -1969,6 +1974,8 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
 
         setup_sigtramp(tramp_page);
         target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC);
+        vdso_sigreturn_region_start = tramp_page;
+        vdso_sigreturn_region_end = tramp_page + TARGET_PAGE_SIZE;
     }
 
     bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &ehdr, info,
diff --git a/linux-user/gen-vdso-elfn.c.inc b/linux-user/gen-vdso-elfn.c.inc
index b47019e136..c2677a146c 100644
--- a/linux-user/gen-vdso-elfn.c.inc
+++ b/linux-user/gen-vdso-elfn.c.inc
@@ -84,9 +84,12 @@ static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx,
 
         if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) {
             sigreturn_addr = sym.st_value;
-        }
-        if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
+        } else if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
             rt_sigreturn_addr = sym.st_value;
+        } else if (strcmp("sigreturn_region_start", name) == 0) {
+            sigreturn_region_start_addr = sym.st_value;
+        } else if (strcmp("sigreturn_region_end", name) == 0) {
+            sigreturn_region_end_addr = sym.st_value;
         }
     }
 }
diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c
index aeaa927db8..d6a2cdaa83 100644
--- a/linux-user/gen-vdso.c
+++ b/linux-user/gen-vdso.c
@@ -36,6 +36,8 @@ static const char *rt_sigreturn_sym;
 
 static unsigned sigreturn_addr;
 static unsigned rt_sigreturn_addr;
+static unsigned sigreturn_region_start_addr;
+static unsigned sigreturn_region_end_addr;
 
 #define N 32
 #define elfN(x)  elf32_##x
@@ -215,6 +217,10 @@ int main(int argc, char **argv)
     fprintf(outf, "    .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix);
     fprintf(outf, "    .sigreturn_ofs = 0x%x,\n", sigreturn_addr);
     fprintf(outf, "    .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr);
+    fprintf(outf, "    .sigreturn_region_start_ofs = 0x%x,\n",
+            sigreturn_region_start_addr);
+    fprintf(outf, "    .sigreturn_region_end_ofs = 0x%x,\n",
+            sigreturn_region_end_addr);
     fprintf(outf, "};\n");
 
     ret = EXIT_SUCCESS;
diff --git a/linux-user/hppa/vdso.S b/linux-user/hppa/vdso.S
index 5be14d2f70..a6f8da2981 100644
--- a/linux-user/hppa/vdso.S
+++ b/linux-user/hppa/vdso.S
@@ -156,8 +156,10 @@
 __kernel_sigtramp_rt:
 	ldi	0, %r25
 	ldi	__NR_rt_sigreturn, %r20
+sigreturn_region_start:
 	be,l	0x100(%sr2, %r0), %sr0, %r31
 	nop
+sigreturn_region_end:
 
 	.cfi_endproc
 	.size	__kernel_sigtramp_rt, . - __kernel_sigtramp_rt
diff --git a/linux-user/hppa/vdso.so b/linux-user/hppa/vdso.so
index e1ddd70c37..68baf80fd3 100755
--- a/linux-user/hppa/vdso.so
+++ b/linux-user/hppa/vdso.so
Binary files differdiff --git a/linux-user/i386/vdso.S b/linux-user/i386/vdso.S
index e7a1f333a1..8df77b5a94 100644
--- a/linux-user/i386/vdso.S
+++ b/linux-user/i386/vdso.S
@@ -114,6 +114,7 @@ vdso_syscall3 __vdso_getcpu, __NR_gettimeofday
  */
 	nop
 
+sigreturn_region_start:
 __kernel_sigreturn:
 	popl	%eax	/* pop sig */
 	.cfi_adjust_cfa_offset -4
@@ -128,6 +129,7 @@ __kernel_rt_sigreturn:
 	movl	$__NR_rt_sigreturn, %eax
 	int	$0x80
 endf	__kernel_rt_sigreturn
+sigreturn_region_end:
 
 	.cfi_endproc
 
diff --git a/linux-user/i386/vdso.so b/linux-user/i386/vdso.so
index bdece5dfcf..e01c3818d0 100755
--- a/linux-user/i386/vdso.so
+++ b/linux-user/i386/vdso.so
Binary files differdiff --git a/linux-user/loader.h b/linux-user/loader.h
index e42b8fa1e3..da9ad28db5 100644
--- a/linux-user/loader.h
+++ b/linux-user/loader.h
@@ -117,6 +117,8 @@ typedef struct {
     unsigned reloc_count;
     unsigned sigreturn_ofs;
     unsigned rt_sigreturn_ofs;
+    unsigned sigreturn_region_start_ofs;
+    unsigned sigreturn_region_end_ofs;
 } VdsoImageInfo;
 
 /* Note that both Elf32_Word and Elf64_Word are uint32_t. */
diff --git a/linux-user/loongarch64/vdso.S b/linux-user/loongarch64/vdso.S
index 780a5fda12..2409d95476 100644
--- a/linux-user/loongarch64/vdso.S
+++ b/linux-user/loongarch64/vdso.S
@@ -125,6 +125,8 @@ vdso_syscall __vdso_getcpu, __NR_getcpu
 
 __vdso_rt_sigreturn:
 	li.w	$a7, __NR_rt_sigreturn
+sigreturn_region_start:
 	syscall	0
+sigreturn_region_end:
 	.cfi_endproc
 endf __vdso_rt_sigreturn
diff --git a/linux-user/loongarch64/vdso.so b/linux-user/loongarch64/vdso.so
index 7c2de6c50e..3704834f0d 100755
--- a/linux-user/loongarch64/vdso.so
+++ b/linux-user/loongarch64/vdso.so
Binary files differdiff --git a/linux-user/main.c b/linux-user/main.c
index 4ddfc9a619..db751c0757 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -228,6 +228,8 @@ void init_task_state(TaskState *ts)
         ts->start_boottime += bt.tv_nsec * (uint64_t) ticks_per_sec /
                               NANOSECONDS_PER_SECOND;
     }
+
+    ts->sys_dispatch_len = -1;
 }
 
 CPUArchState *cpu_copy(CPUArchState *env)
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 002e1e668e..847092a28a 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -22,8 +22,6 @@
 #include "exec/log.h"
 #include "exec/page-protection.h"
 #include "exec/mmap-lock.h"
-#include "exec/tb-flush.h"
-#include "exec/translation-block.h"
 #include "qemu.h"
 #include "user/page-protection.h"
 #include "user-internals.h"
@@ -1007,11 +1005,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
      * be atomic with respect to an external process.
      */
     if (ret != -1 && (flags & MAP_TYPE) != MAP_PRIVATE) {
-        CPUState *cpu = thread_cpu;
-        if (!tcg_cflags_has(cpu, CF_PARALLEL)) {
-            tcg_cflags_set(cpu, CF_PARALLEL);
-            tb_flush(cpu);
-        }
+        begin_parallel_context(thread_cpu);
     }
 
     return ret;
@@ -1448,10 +1442,7 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
      * supported by the host -- anything that requires EXCP_ATOMIC will not
      * be atomic with respect to an external process.
      */
-    if (!tcg_cflags_has(cpu, CF_PARALLEL)) {
-        tcg_cflags_set(cpu, CF_PARALLEL);
-        tb_flush(cpu);
-    }
+    begin_parallel_context(cpu);
 
     if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
         FILE *f = qemu_log_trylock();
diff --git a/linux-user/ppc/vdso-32.so b/linux-user/ppc/vdso-32.so
index 0dc55e0ddd..03476052fb 100755
--- a/linux-user/ppc/vdso-32.so
+++ b/linux-user/ppc/vdso-32.so
Binary files differdiff --git a/linux-user/ppc/vdso-64.so b/linux-user/ppc/vdso-64.so
index ac1ab2582e..b89f2a0e01 100755
--- a/linux-user/ppc/vdso-64.so
+++ b/linux-user/ppc/vdso-64.so
Binary files differdiff --git a/linux-user/ppc/vdso-64le.so b/linux-user/ppc/vdso-64le.so
index 424abb4290..22499d2701 100755
--- a/linux-user/ppc/vdso-64le.so
+++ b/linux-user/ppc/vdso-64le.so
Binary files differdiff --git a/linux-user/ppc/vdso.S b/linux-user/ppc/vdso.S
index 2e79ea9808..e9256a2dea 100644
--- a/linux-user/ppc/vdso.S
+++ b/linux-user/ppc/vdso.S
@@ -220,6 +220,7 @@ endf	__kernel_sync_dicache
 
 	nop
 
+sigreturn_region_start:
 __kernel_sigtramp_rt:
 	raw_syscall __NR_rt_sigreturn
 endf	__kernel_sigtramp_rt
@@ -235,5 +236,6 @@ __kernel_sigtramp32:
 	raw_syscall __NR_sigreturn
 endf	__kernel_sigtramp32
 #endif
+sigreturn_region_end:
 
 	.cfi_endproc
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index e4dca0c20f..cabb7bd6a8 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -155,6 +155,11 @@ struct TaskState {
     /* This thread's sigaltstack, if it has one */
     struct target_sigaltstack sigaltstack_used;
 
+    /* This thread's SYSCALL_USER_DISPATCH state, len=~0 means disabled */
+    vaddr sys_dispatch;
+    vaddr sys_dispatch_selector;
+    abi_ulong sys_dispatch_len;
+
     /* Start time of task after system boot in clock ticks */
     uint64_t start_boottime;
 };
diff --git a/linux-user/riscv/vdso-32.so b/linux-user/riscv/vdso-32.so
index c2ce2a4757..4818a994f0 100755
--- a/linux-user/riscv/vdso-32.so
+++ b/linux-user/riscv/vdso-32.so
Binary files differdiff --git a/linux-user/riscv/vdso-64.so b/linux-user/riscv/vdso-64.so
index ae49f5b043..cc6f7e974b 100755
--- a/linux-user/riscv/vdso-64.so
+++ b/linux-user/riscv/vdso-64.so
Binary files differdiff --git a/linux-user/riscv/vdso.S b/linux-user/riscv/vdso.S
index c37275233a..1d780db771 100644
--- a/linux-user/riscv/vdso.S
+++ b/linux-user/riscv/vdso.S
@@ -181,7 +181,9 @@ endf __vdso_flush_icache
 	nop
 
 __vdso_rt_sigreturn:
+sigreturn_region_start:
 	raw_syscall __NR_rt_sigreturn
+sigreturn_region_end:
 endf __vdso_rt_sigreturn
 
 	.cfi_endproc
diff --git a/linux-user/s390x/vdso.S b/linux-user/s390x/vdso.S
index 3332492477..c60e9ed086 100644
--- a/linux-user/s390x/vdso.S
+++ b/linux-user/s390x/vdso.S
@@ -52,6 +52,7 @@ vdso_syscall __kernel_getcpu, __NR_getcpu
  * by all users.  Without it we get the fallback signal frame handling.
  */
 
+sigreturn_region_start:
 __kernel_sigreturn:
 	raw_syscall __NR_sigreturn
 endf	__kernel_sigreturn
@@ -59,3 +60,4 @@ endf	__kernel_sigreturn
 __kernel_rt_sigreturn:
 	raw_syscall __NR_rt_sigreturn
 endf	__kernel_rt_sigreturn
+sigreturn_region_end:
diff --git a/linux-user/s390x/vdso.so b/linux-user/s390x/vdso.so
index 64130f6f33..a669a6b7dd 100755
--- a/linux-user/s390x/vdso.so
+++ b/linux-user/s390x/vdso.so
Binary files differdiff --git a/linux-user/signal-common.h b/linux-user/signal-common.h
index 196d2406f8..8a44714251 100644
--- a/linux-user/signal-common.h
+++ b/linux-user/signal-common.h
@@ -25,6 +25,13 @@
 /* Fallback addresses into sigtramp page. */
 extern abi_ulong default_sigreturn;
 extern abi_ulong default_rt_sigreturn;
+extern abi_ulong vdso_sigreturn_region_start;
+extern abi_ulong vdso_sigreturn_region_end;
+
+static inline bool is_vdso_sigreturn(abi_ulong pc)
+{
+    return pc >= vdso_sigreturn_region_start && pc < vdso_sigreturn_region_end;
+}
 
 void setup_sigtramp(abi_ulong tramp_page);
 
diff --git a/linux-user/signal.c b/linux-user/signal.c
index cd0e7398aa..804096bd44 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -50,6 +50,8 @@ static void host_signal_handler(int host_signum, siginfo_t *info,
 /* Fallback addresses into sigtramp page. */
 abi_ulong default_sigreturn;
 abi_ulong default_rt_sigreturn;
+abi_ulong vdso_sigreturn_region_start;
+abi_ulong vdso_sigreturn_region_end;
 
 /*
  * System includes define _NSIG as SIGRTMAX + 1, but qemu (like the kernel)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 91360a072c..1a5f2a03f9 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -27,8 +27,6 @@
 #include "target_mman.h"
 #include "exec/page-protection.h"
 #include "exec/mmap-lock.h"
-#include "exec/tb-flush.h"
-#include "exec/translation-block.h"
 #include <elf.h>
 #include <endian.h>
 #include <grp.h>
@@ -6344,6 +6342,10 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
 #endif
 #ifndef PR_SET_SYSCALL_USER_DISPATCH
 # define PR_SET_SYSCALL_USER_DISPATCH 59
+# define PR_SYS_DISPATCH_OFF 0
+# define PR_SYS_DISPATCH_ON 1
+# define SYSCALL_DISPATCH_FILTER_ALLOW 0
+# define SYSCALL_DISPATCH_FILTER_BLOCK 1
 #endif
 #ifndef PR_SME_SET_VL
 # define PR_SME_SET_VL  63
@@ -6398,6 +6400,36 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2)
 #define do_prctl_sme_set_vl do_prctl_inval1
 #endif
 
+static abi_long do_prctl_syscall_user_dispatch(CPUArchState *env,
+                                               abi_ulong arg2, abi_ulong arg3,
+                                               abi_ulong arg4, abi_ulong arg5)
+{
+    CPUState *cpu = env_cpu(env);
+    TaskState *ts = get_task_state(cpu);
+
+    switch (arg2) {
+    case PR_SYS_DISPATCH_OFF:
+        if (arg3 || arg4 || arg5) {
+            return -TARGET_EINVAL;
+        }
+        ts->sys_dispatch_len = -1;
+        return 0;
+    case PR_SYS_DISPATCH_ON:
+        if (arg3 && arg3 + arg4 <= arg3) {
+            return -TARGET_EINVAL;
+        }
+        if (arg5 && !access_ok(cpu, VERIFY_READ, arg5, 1)) {
+            return -TARGET_EFAULT;
+        }
+        ts->sys_dispatch = arg3;
+        ts->sys_dispatch_len = arg4;
+        ts->sys_dispatch_selector = arg5;
+        return 0;
+    default:
+        return -TARGET_EINVAL;
+    }
+}
+
 static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
                          abi_long arg3, abi_long arg4, abi_long arg5)
 {
@@ -6473,6 +6505,9 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
     case PR_SET_UNALIGN:
         return do_prctl_set_unalign(env, arg2);
 
+    case PR_SET_SYSCALL_USER_DISPATCH:
+        return do_prctl_syscall_user_dispatch(env, arg2, arg3, arg4, arg5);
+
     case PR_CAP_AMBIENT:
     case PR_CAPBSET_READ:
     case PR_CAPBSET_DROP:
@@ -6527,7 +6562,6 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
     case PR_SET_MM:
     case PR_GET_SECCOMP:
     case PR_SET_SECCOMP:
-    case PR_SET_SYSCALL_USER_DISPATCH:
     case PR_GET_THP_DISABLE:
     case PR_SET_THP_DISABLE:
     case PR_GET_TSC:
@@ -6631,10 +6665,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
          * generate code for parallel execution and flush old translations.
          * Do this now so that the copy gets CF_PARALLEL too.
          */
-        if (!tcg_cflags_has(cpu, CF_PARALLEL)) {
-            tcg_cflags_set(cpu, CF_PARALLEL);
-            tb_flush(cpu);
-        }
+        begin_parallel_context(cpu);
 
         /* we create a new CPU instance. */
         new_env = cpu_copy(env);
@@ -13897,12 +13928,46 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
     return ret;
 }
 
+static bool sys_dispatch(CPUState *cpu, TaskState *ts)
+{
+    abi_ptr pc;
+
+    if (likely(ts->sys_dispatch_len == -1)) {
+        return false;
+    }
+
+    pc = cpu->cc->get_pc(cpu);
+    if (likely(pc - ts->sys_dispatch < ts->sys_dispatch_len)) {
+        return false;
+    }
+    if (unlikely(is_vdso_sigreturn(pc))) {
+        return false;
+    }
+    if (likely(ts->sys_dispatch_selector)) {
+        uint8_t sb;
+        if (get_user_u8(sb, ts->sys_dispatch_selector)) {
+            force_sig(TARGET_SIGSEGV);
+            return true;
+        }
+        if (likely(sb == SYSCALL_DISPATCH_FILTER_ALLOW)) {
+            return false;
+        }
+        if (unlikely(sb != SYSCALL_DISPATCH_FILTER_BLOCK)) {
+            force_sig(TARGET_SIGSYS);
+            return true;
+        }
+    }
+    force_sig_fault(TARGET_SIGSYS, TARGET_SYS_USER_DISPATCH, pc);
+    return true;
+}
+
 abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1,
                     abi_long arg2, abi_long arg3, abi_long arg4,
                     abi_long arg5, abi_long arg6, abi_long arg7,
                     abi_long arg8)
 {
     CPUState *cpu = env_cpu(cpu_env);
+    TaskState *ts = get_task_state(cpu);
     abi_long ret;
 
 #ifdef DEBUG_ERESTARTSYS
@@ -13919,6 +13984,10 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1,
     }
 #endif
 
+    if (sys_dispatch(cpu, ts)) {
+        return -QEMU_ESIGRETURN;
+    }
+
     record_syscall_start(cpu, num, arg1,
                          arg2, arg3, arg4, arg5, arg6, arg7, arg8);
 
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index df26a2d28f..cd9ff709b8 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -690,6 +690,12 @@ typedef struct target_siginfo {
 #define TARGET_TRAP_UNK         (5)     /* undiagnosed trap */
 
 /*
+ * SIGSYS si_codes
+ */
+#define TARGET_SYS_SECCOMP       (1)  /* seccomp triggered */
+#define TARGET_SYS_USER_DISPATCH (2)  /* syscall user dispatch triggered */
+
+/*
  * SIGEMT si_codes
  */
 #define TARGET_EMT_TAGOVF      1       /* tag overflow */
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index 691b9a1775..7099349ec8 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -20,6 +20,8 @@
 
 #include "user/thunk.h"
 #include "qemu/log.h"
+#include "exec/tb-flush.h"
+#include "exec/translation-block.h"
 
 extern char *exec_path;
 void init_task_state(TaskState *ts);
@@ -172,6 +174,20 @@ static inline int regpairs_aligned(CPUArchState *cpu_env, int num) { return 0; }
  */
 void preexit_cleanup(CPUArchState *env, int code);
 
+/**
+ * begin_parallel_context
+ * @cs: the CPU context
+ *
+ * Called when starting the second vcpu, or joining shared memory.
+ */
+static inline void begin_parallel_context(CPUState *cs)
+{
+    if (!tcg_cflags_has(cs, CF_PARALLEL)) {
+        tb_flush__exclusive_or_serial();
+        tcg_cflags_set(cs, CF_PARALLEL);
+    }
+}
+
 /*
  * Include target-specific struct and function definitions;
  * they may need access to the target-independent structures