From 56d190848b9b8ecb8213835b0d2d05b243bcb5b8 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Wed, 28 Oct 2020 22:38:33 +0100 Subject: [PATCH 01/14] linux-user/mmap.c: check range of mremap result in target address space If mremap succeeds, an additional check is performed to ensure that the new address range fits into the target address space. This check was previously perfomed in host address space, with the upper bound fixed to abi_ulong. This patch replaces the static check with a call to `guest_range_valid`, performing the range check against the actual size of the target address space. It also moves the corresponding block to prevent it from being called incorrectly when the mapping itself fails. Signed-off-by: Tobias Koch Message-Id: <20201028213833.26592-1-tobias.koch@nonterra.com> Signed-off-by: Laurent Vivier --- linux-user/mmap.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 00c05e6a0f..810653c503 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -767,20 +767,23 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, } if (prot == 0) { host_addr = mremap(g2h(old_addr), old_size, new_size, flags); - if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) { - mmap_reserve(old_addr + old_size, old_size - new_size); + + if (host_addr != MAP_FAILED) { + /* Check if address fits target address space */ + if (!guest_range_valid(h2g(host_addr), new_size)) { + /* Revert mremap() changes */ + host_addr = mremap(g2h(old_addr), new_size, old_size, + flags); + errno = ENOMEM; + host_addr = MAP_FAILED; + } else if (reserved_va && old_size > new_size) { + mmap_reserve(old_addr + old_size, old_size - new_size); + } } } else { errno = ENOMEM; host_addr = MAP_FAILED; } - /* Check if address fits target address space */ - if ((unsigned long)host_addr + new_size > (abi_ulong)-1) { - /* Revert mremap() changes */ - host_addr = mremap(g2h(old_addr), new_size, old_size, flags); - errno = ENOMEM; - host_addr = MAP_FAILED; - } } if (host_addr == MAP_FAILED) { From 6dd97bfc1fd4453c4855109dd508a78617527a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 14 Dec 2020 01:32:10 +0100 Subject: [PATCH 02/14] linux-user/elfload: Move GET_FEATURE macro out of get_elf_hwcap() body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are going to add more macros, keep the function body clear. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20201214003215.344522-2-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0b02a92602..aae28fd929 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -989,22 +989,22 @@ enum { #define ELF_HWCAP get_elf_hwcap() +#define GET_FEATURE(_flag, _hwcap) \ + do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) + static uint32_t get_elf_hwcap(void) { MIPSCPU *cpu = MIPS_CPU(thread_cpu); uint32_t hwcaps = 0; -#define GET_FEATURE(flag, hwcap) \ - do { if (cpu->env.insn_flags & (flag)) { hwcaps |= hwcap; } } while (0) - GET_FEATURE(ISA_MIPS32R6 | ISA_MIPS64R6, HWCAP_MIPS_R6); GET_FEATURE(ASE_MSA, HWCAP_MIPS_MSA); -#undef GET_FEATURE - return hwcaps; } +#undef GET_FEATURE + #endif /* TARGET_MIPS */ #ifdef TARGET_MICROBLAZE From 7d9a3d96f57dfed441622ebb9d1516473d51f919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 14 Dec 2020 01:32:11 +0100 Subject: [PATCH 03/14] linux-user/elfload: Rename MIPS GET_FEATURE() as GET_FEATURE_INSN() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to add macros similar to GET_FEATURE(). As this one use the 'insn_flags' field, rename it GET_FEATURE_INSN(). Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20201214003215.344522-3-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index aae28fd929..0e1d7e7677 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -989,7 +989,7 @@ enum { #define ELF_HWCAP get_elf_hwcap() -#define GET_FEATURE(_flag, _hwcap) \ +#define GET_FEATURE_INSN(_flag, _hwcap) \ do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) static uint32_t get_elf_hwcap(void) @@ -997,13 +997,13 @@ static uint32_t get_elf_hwcap(void) MIPSCPU *cpu = MIPS_CPU(thread_cpu); uint32_t hwcaps = 0; - GET_FEATURE(ISA_MIPS32R6 | ISA_MIPS64R6, HWCAP_MIPS_R6); - GET_FEATURE(ASE_MSA, HWCAP_MIPS_MSA); + GET_FEATURE_INSN(ISA_MIPS32R6 | ISA_MIPS64R6, HWCAP_MIPS_R6); + GET_FEATURE_INSN(ASE_MSA, HWCAP_MIPS_MSA); return hwcaps; } -#undef GET_FEATURE +#undef GET_FEATURE_INSN #endif /* TARGET_MIPS */ From 388765a05bde86de9d9b66348afed6551c58f091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 14 Dec 2020 01:32:12 +0100 Subject: [PATCH 04/14] linux-user/elfload: Introduce MIPS GET_FEATURE_REG_SET() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISA features are usually denoted in read-only bits from CPU registers. Add the GET_FEATURE_REG_SET() macro which checks if a CPU register has bits set. Use the macro to check for MSA (which sets the MSAP bit of the Config3 register when the ASE implementation is present). Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20201214003215.344522-4-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0e1d7e7677..b7c6d30723 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -992,17 +992,21 @@ enum { #define GET_FEATURE_INSN(_flag, _hwcap) \ do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) +#define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ + do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) + static uint32_t get_elf_hwcap(void) { MIPSCPU *cpu = MIPS_CPU(thread_cpu); uint32_t hwcaps = 0; GET_FEATURE_INSN(ISA_MIPS32R6 | ISA_MIPS64R6, HWCAP_MIPS_R6); - GET_FEATURE_INSN(ASE_MSA, HWCAP_MIPS_MSA); + GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); return hwcaps; } +#undef GET_FEATURE_REG_SET #undef GET_FEATURE_INSN #endif /* TARGET_MIPS */ From ce54384405b77483f5ce06ab8dc7537299453b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 14 Dec 2020 01:32:13 +0100 Subject: [PATCH 05/14] linux-user/elfload: Introduce MIPS GET_FEATURE_REG_EQU() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISA features are usually denoted in read-only bits from CPU registers. Add the GET_FEATURE_REG_EQU() macro which checks if a CPU register has bits set to a specific value. Use the macro to check the 'Architecture Revision' level of the Config0 register, which is '2' when the Release 6 ISA is implemented. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20201214003215.344522-5-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 12 +++++++++++- target/mips/cpu.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index b7c6d30723..8f943f93ba 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -7,6 +7,7 @@ #include "qemu.h" #include "disas/disas.h" +#include "qemu/bitops.h" #include "qemu/path.h" #include "qemu/queue.h" #include "qemu/guest-random.h" @@ -995,17 +996,26 @@ enum { #define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) +#define GET_FEATURE_REG_EQU(_reg, _start, _length, _val, _hwcap) \ + do { \ + if (extract32(cpu->env._reg, (_start), (_length)) == (_val)) { \ + hwcaps |= _hwcap; \ + } \ + } while (0) + static uint32_t get_elf_hwcap(void) { MIPSCPU *cpu = MIPS_CPU(thread_cpu); uint32_t hwcaps = 0; - GET_FEATURE_INSN(ISA_MIPS32R6 | ISA_MIPS64R6, HWCAP_MIPS_R6); + GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, + 2, HWCAP_MIPS_R6); GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); return hwcaps; } +#undef GET_FEATURE_REG_EQU #undef GET_FEATURE_REG_SET #undef GET_FEATURE_INSN diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 3ac21d0e9c..4cbc31c3e8 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -844,6 +844,7 @@ struct CPUMIPSState { #define CP0C0_MT 7 /* 9..7 */ #define CP0C0_VI 3 #define CP0C0_K0 0 /* 2..0 */ +#define CP0C0_AR_LENGTH 3 int32_t CP0_Config1; #define CP0C1_M 31 #define CP0C1_MMU 25 /* 30..25 */ From 9ea313ba5d2071f6c3bf0897a7876c7c527964d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 14 Dec 2020 01:32:14 +0100 Subject: [PATCH 06/14] linux-user/elfload: Update HWCAP bits from linux 5.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20201214003215.344522-6-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8f943f93ba..0836e72b5a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -986,6 +986,19 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e enum { HWCAP_MIPS_R6 = (1 << 0), HWCAP_MIPS_MSA = (1 << 1), + HWCAP_MIPS_CRC32 = (1 << 2), + HWCAP_MIPS_MIPS16 = (1 << 3), + HWCAP_MIPS_MDMX = (1 << 4), + HWCAP_MIPS_MIPS3D = (1 << 5), + HWCAP_MIPS_SMARTMIPS = (1 << 6), + HWCAP_MIPS_DSP = (1 << 7), + HWCAP_MIPS_DSP2 = (1 << 8), + HWCAP_MIPS_DSP3 = (1 << 9), + HWCAP_MIPS_MIPS16E2 = (1 << 10), + HWCAP_LOONGSON_MMI = (1 << 11), + HWCAP_LOONGSON_EXT = (1 << 12), + HWCAP_LOONGSON_EXT2 = (1 << 13), + HWCAP_LOONGSON_CPUCFG = (1 << 14), }; #define ELF_HWCAP get_elf_hwcap() From 53673d0ff4a45964322bd6bfe904eff7dc96197d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 14 Dec 2020 01:32:15 +0100 Subject: [PATCH 07/14] linux-user: Add support for MIPS Loongson 2F/3A MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Userland ELF binaries using Loongson SIMD instructions have the HWCAP_LOONGSON_MMI bit set [1]. Binaries compiled for Loongson 3A [2] have the HWCAP_LOONGSON_EXT bit set for the LQ / SQ instructions. [1] commit 8e2d5831e4b ("target/mips: Legalize Loongson insn flags") [2] commit af868995e1b ("target/mips: Add Loongson-3 CPU definition") Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20201214003215.344522-7-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- linux-user/elfload.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0836e72b5a..a64050713f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1024,6 +1024,8 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, 2, HWCAP_MIPS_R6); GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); + GET_FEATURE_INSN(ASE_LMMI, HWCAP_LOONGSON_MMI); + GET_FEATURE_INSN(ASE_LEXT, HWCAP_LOONGSON_EXT); return hwcaps; } From c8a03a8f95781fe3dfbcc35967e7d9c7e2506dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Nov 2020 17:08:38 +0100 Subject: [PATCH 08/14] docs/user: Display linux-user binaries nicely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit linux-user binaries are displayed altogether. Use the '*' character to force displaying them as bullet list (one list per architecture). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-Id: <20201119160838.1981709-1-f4bug@amsat.org> Signed-off-by: Laurent Vivier --- docs/user/main.rst | 99 ++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index bd99b0fdbe..8dfe232a3a 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -170,68 +170,81 @@ QEMU_STRACE Other binaries ~~~~~~~~~~~~~~ -user mode (Alpha) -``qemu-alpha`` TODO. +- user mode (Alpha) -user mode (Arm) -``qemu-armeb`` TODO. + * ``qemu-alpha`` TODO. -user mode (Arm) -``qemu-arm`` is also capable of running Arm \"Angel\" semihosted ELF -binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB -configurations), and arm-uclinux bFLT format binaries. +- user mode (Arm) -user mode (ColdFire) -user mode (M68K) -``qemu-m68k`` is capable of running semihosted binaries using the BDM -(m5xxx-ram-hosted.ld) or m68k-sim (sim.ld) syscall interfaces, and -coldfire uClinux bFLT format binaries. + * ``qemu-armeb`` TODO. -The binary format is detected automatically. + * ``qemu-arm`` is also capable of running Arm \"Angel\" semihosted ELF + binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB + configurations), and arm-uclinux bFLT format binaries. -user mode (Cris) -``qemu-cris`` TODO. +- user mode (ColdFire) -user mode (i386) -``qemu-i386`` TODO. ``qemu-x86_64`` TODO. +- user mode (M68K) -user mode (Microblaze) -``qemu-microblaze`` TODO. + * ``qemu-m68k`` is capable of running semihosted binaries using the BDM + (m5xxx-ram-hosted.ld) or m68k-sim (sim.ld) syscall interfaces, and + coldfire uClinux bFLT format binaries. -user mode (MIPS) -``qemu-mips`` executes 32-bit big endian MIPS binaries (MIPS O32 ABI). + The binary format is detected automatically. -``qemu-mipsel`` executes 32-bit little endian MIPS binaries (MIPS O32 -ABI). +- user mode (Cris) -``qemu-mips64`` executes 64-bit big endian MIPS binaries (MIPS N64 ABI). + * ``qemu-cris`` TODO. -``qemu-mips64el`` executes 64-bit little endian MIPS binaries (MIPS N64 -ABI). +- user mode (i386) -``qemu-mipsn32`` executes 32-bit big endian MIPS binaries (MIPS N32 -ABI). + * ``qemu-i386`` TODO. + * ``qemu-x86_64`` TODO. -``qemu-mipsn32el`` executes 32-bit little endian MIPS binaries (MIPS N32 -ABI). +- user mode (Microblaze) -user mode (NiosII) -``qemu-nios2`` TODO. + * ``qemu-microblaze`` TODO. -user mode (PowerPC) -``qemu-ppc64abi32`` TODO. ``qemu-ppc64`` TODO. ``qemu-ppc`` TODO. +- user mode (MIPS) -user mode (SH4) -``qemu-sh4eb`` TODO. ``qemu-sh4`` TODO. + * ``qemu-mips`` executes 32-bit big endian MIPS binaries (MIPS O32 ABI). -user mode (SPARC) -``qemu-sparc`` can execute Sparc32 binaries (Sparc32 CPU, 32 bit ABI). + * ``qemu-mipsel`` executes 32-bit little endian MIPS binaries (MIPS O32 ABI). -``qemu-sparc32plus`` can execute Sparc32 and SPARC32PLUS binaries -(Sparc64 CPU, 32 bit ABI). + * ``qemu-mips64`` executes 64-bit big endian MIPS binaries (MIPS N64 ABI). -``qemu-sparc64`` can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and -SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). + * ``qemu-mips64el`` executes 64-bit little endian MIPS binaries (MIPS N64 + ABI). + + * ``qemu-mipsn32`` executes 32-bit big endian MIPS binaries (MIPS N32 ABI). + + * ``qemu-mipsn32el`` executes 32-bit little endian MIPS binaries (MIPS N32 + ABI). + +- user mode (NiosII) + + * ``qemu-nios2`` TODO. + +- user mode (PowerPC) + + * ``qemu-ppc64abi32`` TODO. + * ``qemu-ppc64`` TODO. + * ``qemu-ppc`` TODO. + +- user mode (SH4) + + * ``qemu-sh4eb`` TODO. + * ``qemu-sh4`` TODO. + +- user mode (SPARC) + + * ``qemu-sparc`` can execute Sparc32 binaries (Sparc32 CPU, 32 bit ABI). + + * ``qemu-sparc32plus`` can execute Sparc32 and SPARC32PLUS binaries + (Sparc64 CPU, 32 bit ABI). + + * ``qemu-sparc64`` can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and + SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). BSD User space emulator ----------------------- From 8494645797ac3c61d8693ac4164a87c8790a8717 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Thu, 12 Nov 2020 12:45:16 +0100 Subject: [PATCH 09/14] linux-user: Implement copy_file_range Signed-off-by: Andreas Schwab Reviewed-by: Laurent Vivier Message-Id: [lv: copy back offset only if there is no error] Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 7bf99beb18..6091a449fb 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -813,6 +813,12 @@ safe_syscall5(int, mq_timedsend, int, mqdes, const char *, msg_ptr, safe_syscall5(int, mq_timedreceive, int, mqdes, char *, msg_ptr, size_t, len, unsigned *, prio, const struct timespec *, timeout) #endif +#if defined(TARGET_NR_copy_file_range) && defined(__NR_copy_file_range) +safe_syscall6(ssize_t, copy_file_range, int, infd, loff_t *, pinoff, + int, outfd, loff_t *, poutoff, size_t, length, + unsigned int, flags) +#endif + /* We do ioctl like this rather than via safe_syscall3 to preserve the * "third argument might be integer or pointer or not present" behaviour of * the libc function. @@ -13065,6 +13071,42 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return get_errno(membarrier(arg1, arg2)); #endif +#if defined(TARGET_NR_copy_file_range) && defined(__NR_copy_file_range) + case TARGET_NR_copy_file_range: + { + loff_t inoff, outoff; + loff_t *pinoff = NULL, *poutoff = NULL; + + if (arg2) { + if (get_user_u64(inoff, arg2)) { + return -TARGET_EFAULT; + } + pinoff = &inoff; + } + if (arg4) { + if (get_user_u64(outoff, arg4)) { + return -TARGET_EFAULT; + } + poutoff = &outoff; + } + ret = get_errno(safe_copy_file_range(arg1, pinoff, arg3, poutoff, + arg5, arg6)); + if (!is_error(ret) && ret > 0) { + if (arg2) { + if (put_user_u64(inoff, arg2)) { + return -TARGET_EFAULT; + } + } + if (arg4) { + if (put_user_u64(outoff, arg4)) { + return -TARGET_EFAULT; + } + } + } + } + return ret; +#endif + default: qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); return -TARGET_ENOSYS; From 6addf06a3c4dad68d8d7032e31714e81b438c7d9 Mon Sep 17 00:00:00 2001 From: Shu-Chun Weng Date: Mon, 28 Sep 2020 18:48:01 -0700 Subject: [PATCH 10/14] linux-user: Add most IFTUN ioctls The three options handling `struct sock_fprog` (TUNATTACHFILTER, TUNDETACHFILTER, and TUNGETFILTER) are not implemented. Linux kernel keeps a user space pointer in them which we cannot correctly handle. Signed-off-by: Josh Kunz Signed-off-by: Shu-Chun Weng Reviewed-by: Laurent Vivier Message-Id: <20200929014801.655524-1-scw@google.com> [lv: use 0 size in unlock_user()] Signed-off-by: Laurent Vivier --- linux-user/ioctls.h | 46 +++++++++++++++++++++++++++++++++++++++ linux-user/syscall.c | 37 +++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 32 +++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 8efb4d38c0..661b5daa9f 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -720,3 +720,49 @@ IOCTL(KCOV_DISABLE, 0, TYPE_NULL) IOCTL(KCOV_INIT_TRACE, IOC_R, TYPE_ULONG) #endif + + IOCTL(TUNSETDEBUG, IOC_W, TYPE_INT) + IOCTL(TUNSETIFF, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) + IOCTL(TUNSETPERSIST, IOC_W, TYPE_INT) + IOCTL(TUNSETOWNER, IOC_W, TYPE_INT) + IOCTL(TUNSETLINK, IOC_W, TYPE_INT) + IOCTL(TUNSETGROUP, IOC_W, TYPE_INT) + IOCTL(TUNGETFEATURES, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(TUNSETOFFLOAD, IOC_W, TYPE_LONG) + IOCTL_SPECIAL(TUNSETTXFILTER, IOC_W, do_ioctl_TUNSETTXFILTER, + /* + * We can't represent `struct tun_filter` in thunk so leaving + * it uninterpreted. do_ioctl_TUNSETTXFILTER will do the + * conversion. + */ + TYPE_PTRVOID) + IOCTL(TUNGETIFF, IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) + IOCTL(TUNGETSNDBUF, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(TUNSETSNDBUF, IOC_W, MK_PTR(TYPE_INT)) + /* + * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel keeps a + * user pointer in TUNATTACHFILTER, which we are not able to correctly handle. + */ + IOCTL(TUNGETVNETHDRSZ, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(TUNSETVNETHDRSZ, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(TUNSETQUEUE, IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) + IOCTL(TUNSETIFINDEX , IOC_W, MK_PTR(TYPE_INT)) + /* TUNGETFILTER is not supported: see TUNATTACHFILTER. */ + IOCTL(TUNSETVNETLE, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(TUNGETVNETLE, IOC_R, MK_PTR(TYPE_INT)) +#ifdef TUNSETVNETBE + IOCTL(TUNSETVNETBE, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(TUNGETVNETBE, IOC_R, MK_PTR(TYPE_INT)) +#endif +#ifdef TUNSETSTEERINGEBPF + IOCTL(TUNSETSTEERINGEBPF, IOC_W, MK_PTR(TYPE_INT)) +#endif +#ifdef TUNSETFILTEREBPF + IOCTL(TUNSETFILTEREBPF, IOC_W, MK_PTR(TYPE_INT)) +#endif +#ifdef TUNSETCARRIER + IOCTL(TUNSETCARRIER, IOC_W, MK_PTR(TYPE_INT)) +#endif +#ifdef TUNGETDEVNETNS + IOCTL(TUNGETDEVNETNS, IOC_R, TYPE_NULL) +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6091a449fb..d182890ff0 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_TIMERFD @@ -5709,6 +5710,42 @@ static abi_long do_ioctl_drm_i915(const IOCTLEntry *ie, uint8_t *buf_temp, #endif +static abi_long do_ioctl_TUNSETTXFILTER(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, int cmd, abi_long arg) +{ + struct tun_filter *filter = (struct tun_filter *)buf_temp; + struct tun_filter *target_filter; + char *target_addr; + + assert(ie->access == IOC_W); + + target_filter = lock_user(VERIFY_READ, arg, sizeof(*target_filter), 1); + if (!target_filter) { + return -TARGET_EFAULT; + } + filter->flags = tswap16(target_filter->flags); + filter->count = tswap16(target_filter->count); + unlock_user(target_filter, arg, 0); + + if (filter->count) { + if (offsetof(struct tun_filter, addr) + filter->count * ETH_ALEN > + MAX_STRUCT_SIZE) { + return -TARGET_EFAULT; + } + + target_addr = lock_user(VERIFY_READ, + arg + offsetof(struct tun_filter, addr), + filter->count * ETH_ALEN, 1); + if (!target_addr) { + return -TARGET_EFAULT; + } + memcpy(filter->addr, target_addr, filter->count * ETH_ALEN); + unlock_user(target_addr, arg + offsetof(struct tun_filter, addr), 0); + } + + return get_errno(safe_ioctl(fd, ie->host_cmd, filter)); +} + IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, ...) \ { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index b934d0b606..a00bfc2647 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -929,6 +929,38 @@ struct target_rtc_pll_info { #define TARGET_SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +/* From */ + +#define TARGET_TUNSETDEBUG TARGET_IOW('T', 201, int) +#define TARGET_TUNSETIFF TARGET_IOW('T', 202, int) +#define TARGET_TUNSETPERSIST TARGET_IOW('T', 203, int) +#define TARGET_TUNSETOWNER TARGET_IOW('T', 204, int) +#define TARGET_TUNSETLINK TARGET_IOW('T', 205, int) +#define TARGET_TUNSETGROUP TARGET_IOW('T', 206, int) +#define TARGET_TUNGETFEATURES TARGET_IOR('T', 207, unsigned int) +#define TARGET_TUNSETOFFLOAD TARGET_IOW('T', 208, unsigned int) +#define TARGET_TUNSETTXFILTER TARGET_IOW('T', 209, unsigned int) +#define TARGET_TUNGETIFF TARGET_IOR('T', 210, unsigned int) +#define TARGET_TUNGETSNDBUF TARGET_IOR('T', 211, int) +#define TARGET_TUNSETSNDBUF TARGET_IOW('T', 212, int) +/* + * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel keeps a + * user pointer in TUNATTACHFILTER, which we are not able to correctly handle. + */ +#define TARGET_TUNGETVNETHDRSZ TARGET_IOR('T', 215, int) +#define TARGET_TUNSETVNETHDRSZ TARGET_IOW('T', 216, int) +#define TARGET_TUNSETQUEUE TARGET_IOW('T', 217, int) +#define TARGET_TUNSETIFINDEX TARGET_IOW('T', 218, unsigned int) +/* TUNGETFILTER is not supported: see TUNATTACHFILTER. */ +#define TARGET_TUNSETVNETLE TARGET_IOW('T', 220, int) +#define TARGET_TUNGETVNETLE TARGET_IOR('T', 221, int) +#define TARGET_TUNSETVNETBE TARGET_IOW('T', 222, int) +#define TARGET_TUNGETVNETBE TARGET_IOR('T', 223, int) +#define TARGET_TUNSETSTEERINGEBPF TARGET_IOR('T', 224, int) +#define TARGET_TUNSETFILTEREBPF TARGET_IOR('T', 225, int) +#define TARGET_TUNSETCARRIER TARGET_IOW('T', 226, int) +#define TARGET_TUNGETDEVNETNS TARGET_IO('T', 227) + /* From */ #define TARGET_RNDGETENTCNT TARGET_IOR('R', 0x00, int) From 246ff44295d0153d1d272b4d4a77c4bf9cc6ad66 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 6 Nov 2020 15:27:35 +0000 Subject: [PATCH 11/14] linux-user/sparc: Correct sparc64_get/set_context() FPU handling The handling of the FPU state in sparc64_get_context() and sparc64_set_context() is not the same as what the kernel actually does: we unconditionally read and write the FP registers and the FSR, GSR and FPRS, but the kernel logic is more complicated: * in get_context the kernel has code for saving FPU registers, but it is hidden inside an "if (fenab) condition and the fenab flag is always set to 0 (inside an "#if 1" which has been in the kernel for over 15 years). So the effect is that the FPU state part is always written as zeroes. * in set_context the kernel looks at the fenab field in the structure from the guest, and only restores the state if it is set; it also looks at the structure's FPRS to see whether either the upper or lower or both halves of the register file have valid data. Bring our implementations into line with the kernel: * in get_context: - clear the entire target_ucontext at the top of the function (as the kernel does) - then don't write the FPU state, so those fields remain zero - this fixes Coverity issue CID 1432305 by deleting the code it was complaining about * in set_context: - check the fenab and the fpsr to decide which parts of the FPU data to restore, if any - instead of setting the FPU registers by doing two 32-bit loads and filling in the .upper and .lower parts of the CPU_Double union separately, just do a 64-bit load of the whole register at once. This fixes Coverity issue CID 1432303 because we now access the dregs[] part of the mcfpu_fregs union rather than the sregs[] part (which is not large enough to actually cover the whole of the data, so we were accessing off the end of sregs[]) We change both functions in a single commit to avoid potentially breaking bisection. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20201106152738.26026-2-peter.maydell@linaro.org> [lv: fix FPRS_DU loop s/31/32/] Signed-off-by: Laurent Vivier --- linux-user/sparc/signal.c | 74 +++++++++++++++++++++++---------------- target/sparc/cpu.h | 4 ++- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index d12adc8e6f..0057b48fad 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -402,8 +402,10 @@ void sparc64_set_context(CPUSPARCState *env) abi_ulong ucp_addr; struct target_ucontext *ucp; target_mc_gregset_t *grp; + target_mc_fpu_t *fpup; abi_ulong pc, npc, tstate; unsigned int i; + unsigned char fenab; ucp_addr = env->regwptr[WREG_O0]; if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { @@ -467,26 +469,42 @@ void sparc64_set_context(CPUSPARCState *env) __get_user(env->regwptr[WREG_FP], &(ucp->tuc_mcontext.mc_fp)); __get_user(env->regwptr[WREG_I7], &(ucp->tuc_mcontext.mc_i7)); - /* FIXME this does not match how the kernel handles the FPU in - * its sparc64_set_context implementation. In particular the FPU - * is only restored if fenab is non-zero in: - * __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab)); - */ - __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs)); - { - uint32_t *src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; - for (i = 0; i < 64; i++, src++) { - if (i & 1) { - __get_user(env->fpr[i/2].l.lower, src); - } else { - __get_user(env->fpr[i/2].l.upper, src); + fpup = &ucp->tuc_mcontext.mc_fpregs; + + __get_user(fenab, &(fpup->mcfpu_enab)); + if (fenab) { + abi_ulong fprs; + + /* + * We use the FPRS from the guest only in deciding whether + * to restore the upper, lower, or both banks of the FPU regs. + * The kernel here writes the FPU register data into the + * process's current_thread_info state and unconditionally + * clears FPRS and TSTATE_PEF: this disables the FPU so that the + * next FPU-disabled trap will copy the data out of + * current_thread_info and into the real FPU registers. + * QEMU doesn't need to handle lazy-FPU-state-restoring like that, + * so we always load the data directly into the FPU registers + * and leave FPRS and TSTATE_PEF alone (so the FPU stays enabled). + * Note that because we (and the kernel) always write zeroes for + * the fenab and fprs in sparc64_get_context() none of this code + * will execute unless the guest manually constructed or changed + * the context structure. + */ + __get_user(fprs, &(fpup->mcfpu_fprs)); + if (fprs & FPRS_DL) { + for (i = 0; i < 16; i++) { + __get_user(env->fpr[i].ll, &(fpup->mcfpu_fregs.dregs[i])); } } + if (fprs & FPRS_DU) { + for (i = 16; i < 32; i++) { + __get_user(env->fpr[i].ll, &(fpup->mcfpu_fregs.dregs[i])); + } + } + __get_user(env->fsr, &(fpup->mcfpu_fsr)); + __get_user(env->gsr, &(fpup->mcfpu_gsr)); } - __get_user(env->fsr, - &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr)); - __get_user(env->gsr, - &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr)); unlock_user_struct(ucp, ucp_addr, 0); return; do_sigsegv: @@ -509,7 +527,9 @@ void sparc64_get_context(CPUSPARCState *env) if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) { goto do_sigsegv; } - + + memset(ucp, 0, sizeof(*ucp)); + mcp = &ucp->tuc_mcontext; grp = &mcp->mc_gregs; @@ -572,19 +592,11 @@ void sparc64_get_context(CPUSPARCState *env) __put_user(env->regwptr[WREG_FP], &(mcp->mc_fp)); __put_user(env->regwptr[WREG_I7], &(mcp->mc_i7)); - { - uint32_t *dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; - for (i = 0; i < 64; i++, dst++) { - if (i & 1) { - __put_user(env->fpr[i/2].l.lower, dst); - } else { - __put_user(env->fpr[i/2].l.upper, dst); - } - } - } - __put_user(env->fsr, &(mcp->mc_fpregs.mcfpu_fsr)); - __put_user(env->gsr, &(mcp->mc_fpregs.mcfpu_gsr)); - __put_user(env->fprs, &(mcp->mc_fpregs.mcfpu_fprs)); + /* + * We don't write out the FPU state. This matches the kernel's + * implementation (which has the code for doing this but + * hidden behind an "if (fenab)" where fenab is always 0). + */ if (err) goto do_sigsegv; diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index b9369398f2..277254732b 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -156,7 +156,9 @@ enum { #define PS_IE (1<<1) #define PS_AG (1<<0) /* v9, zero on UA2007 */ -#define FPRS_FEF (1<<2) +#define FPRS_DL (1 << 0) +#define FPRS_DU (1 << 1) +#define FPRS_FEF (1 << 2) #define HS_PRIV (1<<2) #endif From 309abce23658c0142430440c7ad1be2a02170ed0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 6 Nov 2020 15:27:36 +0000 Subject: [PATCH 12/14] linux-user/sparc: Remove unneeded checks of 'err' from sparc64_get_context() Unlike the kernel macros, our __get_user() and __put_user() do not return a failure code. Kernel code typically has a style of err |= __get_user(...); err |= __get_user(...); and then checking err at the end. In sparc64_get_context() our version of the code dropped the accumulating into err but left the "if (err) goto do_sigsegv" checks, which will never be taken. Delete unnecessary if()s. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20201106152738.26026-3-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/sparc/signal.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 0057b48fad..58b48afe29 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -555,8 +555,6 @@ void sparc64_get_context(CPUSPARCState *env) for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { __put_user(*src, dst); } - if (err) - goto do_sigsegv; } /* XXX: tstate must be saved properly */ @@ -598,8 +596,6 @@ void sparc64_get_context(CPUSPARCState *env) * hidden behind an "if (fenab)" where fenab is always 0). */ - if (err) - goto do_sigsegv; unlock_user_struct(ucp, ucp_addr, 1); return; do_sigsegv: From 0ad20314f1e11acaeedcb14135b178a5711766f5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 6 Nov 2020 15:27:37 +0000 Subject: [PATCH 13/14] linux-user/sparc: Don't restore %g7 in sparc64_set_context() The kernel does not restore the g7 register in sparc64_set_context(); neither should we. (We still save it in sparc64_get_context().) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20201106152738.26026-4-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/sparc/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 58b48afe29..d92e096caf 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -447,7 +447,7 @@ void sparc64_set_context(CPUSPARCState *env) __get_user(env->gregs[4], (&(*grp)[SPARC_MC_G4])); __get_user(env->gregs[5], (&(*grp)[SPARC_MC_G5])); __get_user(env->gregs[6], (&(*grp)[SPARC_MC_G6])); - __get_user(env->gregs[7], (&(*grp)[SPARC_MC_G7])); + /* Skip g7 as that's the thread register in userspace */ /* * Note that unlike the kernel, we didn't need to mess with the From 7a5805a08f942325b373643099f784cdac65c9ea Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 6 Nov 2020 15:27:38 +0000 Subject: [PATCH 14/14] linux-user/sparc: Handle tstate in sparc64_get/set_context() Correctly implement save/restore of the tstate field in sparc64_get_context() and sparc64_set_context(): * Don't use the CWP value from the guest in set_context * Construct and save a tstate value rather than leaving it as zero in get_context To do this we factor out the "calculate TSTATE value from CPU state" code from sparc_cpu_do_interrupt() into its own sparc64_tstate() function; that in turn requires us to move some of the function prototypes out from inside a CPU_NO_IO_DEFS ifdef guard. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20201106152738.26026-5-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/sparc/signal.c | 7 +++---- target/sparc/cpu.h | 24 ++++++++++++++++++++---- target/sparc/int64_helper.c | 5 +---- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index d92e096caf..d27b7a3af7 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -438,9 +438,9 @@ void sparc64_set_context(CPUSPARCState *env) env->npc = npc; __get_user(env->y, &((*grp)[SPARC_MC_Y])); __get_user(tstate, &((*grp)[SPARC_MC_TSTATE])); + /* Honour TSTATE_ASI, TSTATE_ICC and TSTATE_XCC only */ env->asi = (tstate >> 24) & 0xff; - cpu_put_ccr(env, tstate >> 32); - cpu_put_cwp64(env, tstate & 0x1f); + cpu_put_ccr(env, (tstate >> 32) & 0xff); __get_user(env->gregs[1], (&(*grp)[SPARC_MC_G1])); __get_user(env->gregs[2], (&(*grp)[SPARC_MC_G2])); __get_user(env->gregs[3], (&(*grp)[SPARC_MC_G3])); @@ -557,8 +557,7 @@ void sparc64_get_context(CPUSPARCState *env) } } - /* XXX: tstate must be saved properly */ - // __put_user(env->tstate, &((*grp)[SPARC_MC_TSTATE])); + __put_user(sparc64_tstate(env), &((*grp)[SPARC_MC_TSTATE])); __put_user(env->pc, &((*grp)[SPARC_MC_PC])); __put_user(env->npc, &((*grp)[SPARC_MC_NPC])); __put_user(env->y, &((*grp)[SPARC_MC_Y])); diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 277254732b..4b2290650b 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -608,10 +608,6 @@ target_ulong cpu_get_psr(CPUSPARCState *env1); void cpu_put_psr(CPUSPARCState *env1, target_ulong val); void cpu_put_psr_raw(CPUSPARCState *env1, target_ulong val); #ifdef TARGET_SPARC64 -target_ulong cpu_get_ccr(CPUSPARCState *env1); -void cpu_put_ccr(CPUSPARCState *env1, target_ulong val); -target_ulong cpu_get_cwp64(CPUSPARCState *env1); -void cpu_put_cwp64(CPUSPARCState *env1, int cwp); void cpu_change_pstate(CPUSPARCState *env1, uint32_t new_pstate); void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl); #endif @@ -829,4 +825,24 @@ static inline bool tb_am_enabled(int tb_flags) #endif } +#ifdef TARGET_SPARC64 +/* win_helper.c */ +target_ulong cpu_get_ccr(CPUSPARCState *env1); +void cpu_put_ccr(CPUSPARCState *env1, target_ulong val); +target_ulong cpu_get_cwp64(CPUSPARCState *env1); +void cpu_put_cwp64(CPUSPARCState *env1, int cwp); + +static inline uint64_t sparc64_tstate(CPUSPARCState *env) +{ + uint64_t tstate = (cpu_get_ccr(env) << 32) | + ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) | + cpu_get_cwp64(env); + + if (env->def.features & CPU_FEATURE_GL) { + tstate |= (env->gl & 7ULL) << 40; + } + return tstate; +} +#endif + #endif diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index ba95bf228c..7fb8ab211c 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -131,9 +131,7 @@ void sparc_cpu_do_interrupt(CPUState *cs) } tsptr = cpu_tsptr(env); - tsptr->tstate = (cpu_get_ccr(env) << 32) | - ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) | - cpu_get_cwp64(env); + tsptr->tstate = sparc64_tstate(env); tsptr->tpc = env->pc; tsptr->tnpc = env->npc; tsptr->tt = intno; @@ -148,7 +146,6 @@ void sparc_cpu_do_interrupt(CPUState *cs) } if (env->def.features & CPU_FEATURE_GL) { - tsptr->tstate |= (env->gl & 7ULL) << 40; cpu_gl_switch_gregs(env, env->gl + 1); env->gl++; }