From 0c49c9180c5498bcd55edc1bfb12b0466e08575c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Sep 2023 17:41:17 +0200 Subject: [PATCH 01/80] target/i386: enumerate bit 56 of MSR_IA32_VMX_BASIC On parts that enumerate IA32_VMX_BASIC MSR bit as 1, any exception vector can be delivered with or without an error code if the other consistency checks are satisfied. Signed-off-by: Paolo Bonzini --- scripts/kvm/vmxcap | 1 + target/i386/cpu.c | 1 + target/i386/cpu.h | 1 + 3 files changed, 3 insertions(+) diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index ce27f5e635..3fb4d5b342 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -115,6 +115,7 @@ controls = [ (50, 53): 'VMCS memory type', 54: 'INS/OUTS instruction information', 55: 'IA32_VMX_TRUE_*_CTLS support', + 56: 'Skip checks on event error code', }, msr = MSR_IA32_VMX_BASIC, ), diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b2a20365e1..d48607b4e1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1340,6 +1340,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .feat_names = { [54] = "vmx-ins-outs", [55] = "vmx-true-ctls", + [56] = "vmx-any-errcode", }, .msr = { .index = MSR_IA32_VMX_BASIC, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index fbb05eace5..d1ffadd78b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1039,6 +1039,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define MSR_VMX_BASIC_DUAL_MONITOR (1ULL << 49) #define MSR_VMX_BASIC_INS_OUTS (1ULL << 54) #define MSR_VMX_BASIC_TRUE_CTLS (1ULL << 55) +#define MSR_VMX_BASIC_ANY_ERRCODE (1ULL << 56) #define MSR_VMX_MISC_PREEMPTION_TIMER_SHIFT_MASK 0x1Full #define MSR_VMX_MISC_STORE_LMA (1ULL << 5) From 3a2a1f97ea349745094e789e6b0768dbd92d0dcd Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Mon, 14 Aug 2023 21:54:27 -0700 Subject: [PATCH 02/80] target/i386: Export GDS_NO bit to guests Gather Data Sampling (GDS) is a side-channel attack using Gather instructions. Some Intel processors will set ARCH_CAP_GDS_NO bit in MSR IA32_ARCH_CAPABILITIES to report that they are not vulnerable to GDS. Make this bit available to guests. Closes: https://lore.kernel.org/qemu-devel/CAMGffEmG6TNq0n3+4OJAgXc8J0OevY60KHZekXCBs3LoK9vehA@mail.gmail.com/ Reported-by: Jack Wang Signed-off-by: Pawan Gupta Tested-by: Jack Wang Tested-by: Daniel Sneddon Message-ID: Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d48607b4e1..f9e51a9d87 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1155,7 +1155,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, "sbdr-ssdp-no", "fbsdp-no", "psdp-no", NULL, "fb-clear", NULL, NULL, NULL, NULL, NULL, NULL, - "pbrsb-no", NULL, NULL, NULL, + "pbrsb-no", NULL, "gds-no", NULL, NULL, NULL, NULL, NULL, }, .msr = { From e8eed838ec93314c164bf3416d80c9c893a2e8ee Mon Sep 17 00:00:00 2001 From: LIU Zhiwei Date: Mon, 11 Sep 2023 14:32:23 +0800 Subject: [PATCH 03/80] qemu/timer: Add host ticks function for RISC-V Signed-off-by: LIU Zhiwei Message-ID: <20230911063223.742-1-zhiwei_liu@linux.alibaba.com> Signed-off-by: Paolo Bonzini --- include/qemu/timer.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 9a91cb1248..9a366e551f 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -979,6 +979,28 @@ static inline int64_t cpu_get_host_ticks(void) return cur - ofs; } +#elif defined(__riscv) && __riscv_xlen == 32 +static inline int64_t cpu_get_host_ticks(void) +{ + uint32_t lo, hi, tmph; + do { + asm volatile("RDTIMEH %0\n\t" + "RDTIME %1\n\t" + "RDTIMEH %2" + : "=r"(hi), "=r"(lo), "=r"(tmph)); + } while (unlikely(tmph != hi)); + return lo | (uint64_t)hi << 32; +} + +#elif defined(__riscv) && __riscv_xlen > 32 +static inline int64_t cpu_get_host_ticks(void) +{ + int64_t val; + + asm volatile("RDTIME %0" : "=r"(val)); + return val; +} + #else /* The host CPU doesn't have an easily accessible cycle counter. Just return a monotonically increasing value. This will be From 1321d84457a36cae0f59d2fcd5aff3af50a93b7d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:36:58 +0100 Subject: [PATCH 04/80] target/m68k: Add URL to semihosting spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spec for m68k semihosting is documented in the libgloss sources. Add a comment with the URL for it, as we already have for nios2 semihosting. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-id: 20230801154451.3505492-1-peter.maydell@linaro.org --- target/m68k/m68k-semi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 239f6e44e9..80cd8d70db 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -15,6 +15,10 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see . + * + * The semihosting protocol implemented here is described in the + * libgloss sources: + * https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=libgloss/m68k/m68k-semi.txt;hb=HEAD */ #include "qemu/osdep.h" From e8d684508efa5c438c89a351b601108a37d08698 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:36:58 +0100 Subject: [PATCH 05/80] docs/devel/loads-stores: Fix git grep regexes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loads-and-stores documentation includes git grep regexes to find occurrences of the various functions. Some of these regexes have errors, typically failing to escape the '?', '(' and ')' when they should be metacharacters (since these are POSIX basic REs). We also weren't consistent about whether to have a ':' on the end of the line introducing the list of regexes in each section. Fix the errors. The following shell rune will complain about any REs in the file which don't have any matches in the codebase: for re in $(sed -ne 's/ - ``\(\\<.*\)``/\1/p' docs/devel/loads-stores.rst); do git grep -q "$re" || echo "no matches for re $re"; done Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20230904161703.3996734-1-peter.maydell@linaro.org --- docs/devel/loads-stores.rst | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index dab6dfa0ac..ec627aa9c0 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -63,12 +63,12 @@ which stores ``val`` to ``ptr`` as an ``{endian}`` order value of size ``sz`` bytes. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` - - ``\`` - - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_mmu`` ~~~~~~~~~~~~~~~~~~~~ @@ -121,8 +121,8 @@ store: ``cpu_st{size}{end}_mmu(env, ptr, val, oi, retaddr)`` - ``_le`` : little endian Regexes for git grep: - - ``\`` - - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_mmuidx_ra`` @@ -155,8 +155,8 @@ store: ``cpu_st{size}{end}_mmuidx_ra(env, ptr, val, mmuidx, retaddr)`` - ``_le`` : little endian Regexes for git grep: - - ``\`` - - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_data_ra`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -193,8 +193,8 @@ store: ``cpu_st{size}{end}_data_ra(env, ptr, val, ra)`` - ``_le`` : little endian Regexes for git grep: - - ``\`` - - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_data`` ~~~~~~~~~~~~~~~~~~~~~ @@ -231,9 +231,9 @@ store: ``cpu_st{size}{end}_data(env, ptr, val)`` - ``_be`` : big endian - ``_le`` : little endian -Regexes for git grep - - ``\`` - - ``\`` +Regexes for git grep: + - ``\`` + - ``\`` ``cpu_ld*_code`` ~~~~~~~~~~~~~~~~ @@ -296,7 +296,7 @@ swap: ``translator_ld{sign}{size}_swap(env, ptr, swap)`` - ``l`` : 32 bits - ``q`` : 64 bits -Regexes for git grep +Regexes for git grep: - ``\`` ``helper_{ld,st}*_mmu`` @@ -325,7 +325,7 @@ store: ``helper_{size}_mmu(env, addr, val, opindex, retaddr)`` - ``l`` : 32 bits - ``q`` : 64 bits -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` @@ -382,7 +382,7 @@ succeeded using a MemTxResult return code. The ``_{endian}`` suffix is omitted for byte accesses. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` @@ -400,7 +400,7 @@ Note that portions of the write which attempt to write data to a device will be silently ignored -- only real RAM and ROM will be written to. -Regexes for git grep +Regexes for git grep: - ``address_space_write_rom`` ``{ld,st}*_phys`` @@ -438,7 +438,7 @@ device doing the access has no way to report such an error. The ``_{endian}_`` infix is omitted for byte accesses. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` @@ -462,7 +462,7 @@ For new code they are better avoided: ``cpu_physical_memory_rw`` -Regexes for git grep +Regexes for git grep: - ``\`` ``cpu_memory_rw_debug`` @@ -497,7 +497,7 @@ make sure our existing code is doing things correctly. ``dma_memory_rw`` -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` @@ -538,7 +538,7 @@ correct address space for that device. The ``_{endian}_`` infix is omitted for byte accesses. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` From 32b214384e1e1472ddfa875196c57f6620172301 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Fri, 15 Sep 2023 15:36:59 +0100 Subject: [PATCH 06/80] hw/arm/boot: Set SCR_EL3.FGTEn when booting kernel Just like d7ef5e16a17c sets SCR_EL3.HXEn for FEAT_HCX, this commit handles SCR_EL3.FGTEn for FEAT_FGT: When we direct boot a kernel on a CPU which emulates EL3, we need to set up the EL3 system registers as the Linux kernel documentation specifies: https://www.kernel.org/doc/Documentation/arm64/booting.rst > For CPUs with the Fine Grained Traps (FEAT_FGT) extension present: > - If EL3 is present and the kernel is entered at EL2: > - SCR_EL3.FGTEn (bit 27) must be initialised to 0b1. Cc: qemu-stable@nongnu.org Signed-off-by: Fabian Vogt Message-id: 4831384.GXAFRqVoOG@linux-e202.suse.de Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/boot.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 720f22531a..24fa169060 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -761,6 +761,10 @@ static void do_cpu_reset(void *opaque) if (cpu_isar_feature(aa64_hcx, cpu)) { env->cp15.scr_el3 |= SCR_HXEN; } + if (cpu_isar_feature(aa64_fgt, cpu)) { + env->cp15.scr_el3 |= SCR_FGTEN; + } + /* AArch64 kernels never boot in secure mode */ assert(!info->secure_boot); /* This hook is only supported for AArch32 currently: From e2e40a7790c33ce540d1047e4f780ac4b5c7c6f2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:36:59 +0100 Subject: [PATCH 07/80] linux-user/elfload.c: Correct SME feature names reported in cpuinfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the names we use for CPU features in linux-user's dummy /proc/cpuinfo don't match the strings in the real kernel in arch/arm64/kernel/cpuinfo.c. Specifically, the SME related features have an underscore in the HWCAP_FOO define name, but (like the SVE ones) they do not have an underscore in the string in cpuinfo. Correct the errors. Fixes: a55b9e7226708 ("linux-user: Emulate /proc/cpuinfo on aarch64 and arm") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- linux-user/elfload.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a5b28fa3e7..5ce009d713 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -844,13 +844,13 @@ const char *elf_hwcap2_str(uint32_t bit) [__builtin_ctz(ARM_HWCAP2_A64_RPRES )] = "rpres", [__builtin_ctz(ARM_HWCAP2_A64_MTE3 )] = "mte3", [__builtin_ctz(ARM_HWCAP2_A64_SME )] = "sme", - [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "sme_i16i64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "sme_f64f64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "sme_i8i32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "sme_f16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "sme_b16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "sme_f32f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "sme_fa64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "smei16i64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "smef64f64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "smei8i32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "smef16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "smeb16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "smef32f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "smefa64", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; From 23d7f14da4cc8cdea31f580f87c90889dcfbe815 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:36:59 +0100 Subject: [PATCH 08/80] linux-user/elfload.c: Add missing arm and arm64 hwcap values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our lists of Arm 32 and 64 bit hwcap values have lagged behind the Linux kernel. Update them to include all the bits defined as of upstream Linux git commit a48fa7efaf1161c1 (in the middle of the kernel 6.6 dev cycle). For 64-bit, we don't yet implement any of the features reported via these hwcap bits. For 32-bit we do in fact already implement them all; we'll add the code to set them in a subsequent commit. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- linux-user/elfload.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5ce009d713..d51d077998 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -402,6 +402,12 @@ enum ARM_HWCAP_ARM_VFPD32 = 1 << 19, ARM_HWCAP_ARM_LPAE = 1 << 20, ARM_HWCAP_ARM_EVTSTRM = 1 << 21, + ARM_HWCAP_ARM_FPHP = 1 << 22, + ARM_HWCAP_ARM_ASIMDHP = 1 << 23, + ARM_HWCAP_ARM_ASIMDDP = 1 << 24, + ARM_HWCAP_ARM_ASIMDFHM = 1 << 25, + ARM_HWCAP_ARM_ASIMDBF16 = 1 << 26, + ARM_HWCAP_ARM_I8MM = 1 << 27, }; enum { @@ -410,6 +416,8 @@ enum { ARM_HWCAP2_ARM_SHA1 = 1 << 2, ARM_HWCAP2_ARM_SHA2 = 1 << 3, ARM_HWCAP2_ARM_CRC32 = 1 << 4, + ARM_HWCAP2_ARM_SB = 1 << 5, + ARM_HWCAP2_ARM_SSBS = 1 << 6, }; /* The commpage only exists for 32 bit kernels */ @@ -540,6 +548,12 @@ const char *elf_hwcap_str(uint32_t bit) [__builtin_ctz(ARM_HWCAP_ARM_VFPD32 )] = "vfpd32", [__builtin_ctz(ARM_HWCAP_ARM_LPAE )] = "lpae", [__builtin_ctz(ARM_HWCAP_ARM_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_ARM_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDFHM )] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDBF16)] = "asimdbf16", + [__builtin_ctz(ARM_HWCAP_ARM_I8MM )] = "i8mm", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; @@ -553,6 +567,8 @@ const char *elf_hwcap2_str(uint32_t bit) [__builtin_ctz(ARM_HWCAP2_ARM_SHA1 )] = "sha1", [__builtin_ctz(ARM_HWCAP2_ARM_SHA2 )] = "sha2", [__builtin_ctz(ARM_HWCAP2_ARM_CRC32)] = "crc32", + [__builtin_ctz(ARM_HWCAP2_ARM_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP2_ARM_SSBS )] = "ssbs", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; @@ -696,6 +712,20 @@ enum { ARM_HWCAP2_A64_SME_B16F32 = 1 << 28, ARM_HWCAP2_A64_SME_F32F32 = 1 << 29, ARM_HWCAP2_A64_SME_FA64 = 1 << 30, + ARM_HWCAP2_A64_WFXT = 1ULL << 31, + ARM_HWCAP2_A64_EBF16 = 1ULL << 32, + ARM_HWCAP2_A64_SVE_EBF16 = 1ULL << 33, + ARM_HWCAP2_A64_CSSC = 1ULL << 34, + ARM_HWCAP2_A64_RPRFM = 1ULL << 35, + ARM_HWCAP2_A64_SVE2P1 = 1ULL << 36, + ARM_HWCAP2_A64_SME2 = 1ULL << 37, + ARM_HWCAP2_A64_SME2P1 = 1ULL << 38, + ARM_HWCAP2_A64_SME_I16I32 = 1ULL << 39, + ARM_HWCAP2_A64_SME_BI32I32 = 1ULL << 40, + ARM_HWCAP2_A64_SME_B16B16 = 1ULL << 41, + ARM_HWCAP2_A64_SME_F16F16 = 1ULL << 42, + ARM_HWCAP2_A64_MOPS = 1ULL << 43, + ARM_HWCAP2_A64_HBC = 1ULL << 44, }; #define ELF_HWCAP get_elf_hwcap() @@ -851,6 +881,20 @@ const char *elf_hwcap2_str(uint32_t bit) [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "smeb16f32", [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "smef32f32", [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "smefa64", + [__builtin_ctz(ARM_HWCAP2_A64_WFXT )] = "wfxt", + [__builtin_ctzll(ARM_HWCAP2_A64_EBF16 )] = "ebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_EBF16 )] = "sveebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_CSSC )] = "cssc", + [__builtin_ctzll(ARM_HWCAP2_A64_RPRFM )] = "rprfm", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE2P1 )] = "sve2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2 )] = "sme2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2P1 )] = "sme2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_I16I32 )] = "smei16i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_BI32I32)] = "smebi32i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_B16B16 )] = "smeb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F16F16 )] = "smef16f16", + [__builtin_ctzll(ARM_HWCAP2_A64_MOPS )] = "mops", + [__builtin_ctzll(ARM_HWCAP2_A64_HBC )] = "hbc", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; From 429b7e0107ddbb1a8103b1cf5c69f6db8795fdc3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:36:59 +0100 Subject: [PATCH 09/80] linux-user/elfload.c: Report previously missing arm32 hwcaps Add the code to report the arm32 hwcaps we were previously missing: ss, ssbs, fphp, asimdhp, asimddp, asimdfhm, asimdbf16, i8mm Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- linux-user/elfload.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d51d077998..bbb4f08109 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -506,6 +506,16 @@ uint32_t get_elf_hwcap(void) } } GET_FEATURE_ID(aa32_simdfmac, ARM_HWCAP_ARM_VFPv4); + /* + * MVFR1.FPHP and .SIMDHP must be in sync, and QEMU uses the same + * isar_feature function for both. The kernel reports them as two hwcaps. + */ + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_FPHP); + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_ASIMDHP); + GET_FEATURE_ID(aa32_dp, ARM_HWCAP_ARM_ASIMDDP); + GET_FEATURE_ID(aa32_fhm, ARM_HWCAP_ARM_ASIMDFHM); + GET_FEATURE_ID(aa32_bf16, ARM_HWCAP_ARM_ASIMDBF16); + GET_FEATURE_ID(aa32_i8mm, ARM_HWCAP_ARM_I8MM); return hwcaps; } @@ -520,6 +530,8 @@ uint32_t get_elf_hwcap2(void) GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1); GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2); GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32); + GET_FEATURE_ID(aa32_sb, ARM_HWCAP2_ARM_SB); + GET_FEATURE_ID(aa32_ssbs, ARM_HWCAP2_ARM_SSBS); return hwcaps; } From 4d9eb296431cfb1dfcaf31fe18d3b1917bc95e6a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:37:00 +0100 Subject: [PATCH 10/80] target/arm: Update AArch64 ID register field definitions Update our AArch64 ID register field definitions from the 2023-06 system register XML release: https://developer.arm.com/documentation/ddi0601/2023-06/ Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/cpu.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index f2e3dc49a6..7ba2402f72 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2166,6 +2166,7 @@ FIELD(ID_AA64ISAR0, SHA1, 8, 4) FIELD(ID_AA64ISAR0, SHA2, 12, 4) FIELD(ID_AA64ISAR0, CRC32, 16, 4) FIELD(ID_AA64ISAR0, ATOMIC, 20, 4) +FIELD(ID_AA64ISAR0, TME, 24, 4) FIELD(ID_AA64ISAR0, RDM, 28, 4) FIELD(ID_AA64ISAR0, SHA3, 32, 4) FIELD(ID_AA64ISAR0, SM3, 36, 4) @@ -2200,6 +2201,13 @@ FIELD(ID_AA64ISAR2, APA3, 12, 4) FIELD(ID_AA64ISAR2, MOPS, 16, 4) FIELD(ID_AA64ISAR2, BC, 20, 4) FIELD(ID_AA64ISAR2, PAC_FRAC, 24, 4) +FIELD(ID_AA64ISAR2, CLRBHB, 28, 4) +FIELD(ID_AA64ISAR2, SYSREG_128, 32, 4) +FIELD(ID_AA64ISAR2, SYSINSTR_128, 36, 4) +FIELD(ID_AA64ISAR2, PRFMSLC, 40, 4) +FIELD(ID_AA64ISAR2, RPRFM, 48, 4) +FIELD(ID_AA64ISAR2, CSSC, 52, 4) +FIELD(ID_AA64ISAR2, ATS1A, 60, 4) FIELD(ID_AA64PFR0, EL0, 0, 4) FIELD(ID_AA64PFR0, EL1, 4, 4) @@ -2227,6 +2235,12 @@ FIELD(ID_AA64PFR1, SME, 24, 4) FIELD(ID_AA64PFR1, RNDR_TRAP, 28, 4) FIELD(ID_AA64PFR1, CSV2_FRAC, 32, 4) FIELD(ID_AA64PFR1, NMI, 36, 4) +FIELD(ID_AA64PFR1, MTE_FRAC, 40, 4) +FIELD(ID_AA64PFR1, GCS, 44, 4) +FIELD(ID_AA64PFR1, THE, 48, 4) +FIELD(ID_AA64PFR1, MTEX, 52, 4) +FIELD(ID_AA64PFR1, DF2, 56, 4) +FIELD(ID_AA64PFR1, PFAR, 60, 4) FIELD(ID_AA64MMFR0, PARANGE, 0, 4) FIELD(ID_AA64MMFR0, ASIDBITS, 4, 4) @@ -2258,6 +2272,7 @@ FIELD(ID_AA64MMFR1, AFP, 44, 4) FIELD(ID_AA64MMFR1, NTLBPA, 48, 4) FIELD(ID_AA64MMFR1, TIDCP1, 52, 4) FIELD(ID_AA64MMFR1, CMOW, 56, 4) +FIELD(ID_AA64MMFR1, ECBHB, 60, 4) FIELD(ID_AA64MMFR2, CNP, 0, 4) FIELD(ID_AA64MMFR2, UAO, 4, 4) @@ -2279,7 +2294,9 @@ FIELD(ID_AA64DFR0, DEBUGVER, 0, 4) FIELD(ID_AA64DFR0, TRACEVER, 4, 4) FIELD(ID_AA64DFR0, PMUVER, 8, 4) FIELD(ID_AA64DFR0, BRPS, 12, 4) +FIELD(ID_AA64DFR0, PMSS, 16, 4) FIELD(ID_AA64DFR0, WRPS, 20, 4) +FIELD(ID_AA64DFR0, SEBEP, 24, 4) FIELD(ID_AA64DFR0, CTX_CMPS, 28, 4) FIELD(ID_AA64DFR0, PMSVER, 32, 4) FIELD(ID_AA64DFR0, DOUBLELOCK, 36, 4) @@ -2287,12 +2304,14 @@ FIELD(ID_AA64DFR0, TRACEFILT, 40, 4) FIELD(ID_AA64DFR0, TRACEBUFFER, 44, 4) FIELD(ID_AA64DFR0, MTPMU, 48, 4) FIELD(ID_AA64DFR0, BRBE, 52, 4) +FIELD(ID_AA64DFR0, EXTTRCBUFF, 56, 4) FIELD(ID_AA64DFR0, HPMN0, 60, 4) FIELD(ID_AA64ZFR0, SVEVER, 0, 4) FIELD(ID_AA64ZFR0, AES, 4, 4) FIELD(ID_AA64ZFR0, BITPERM, 16, 4) FIELD(ID_AA64ZFR0, BFLOAT16, 20, 4) +FIELD(ID_AA64ZFR0, B16B16, 24, 4) FIELD(ID_AA64ZFR0, SHA3, 32, 4) FIELD(ID_AA64ZFR0, SM4, 40, 4) FIELD(ID_AA64ZFR0, I8MM, 44, 4) @@ -2300,9 +2319,13 @@ FIELD(ID_AA64ZFR0, F32MM, 52, 4) FIELD(ID_AA64ZFR0, F64MM, 56, 4) FIELD(ID_AA64SMFR0, F32F32, 32, 1) +FIELD(ID_AA64SMFR0, BI32I32, 33, 1) FIELD(ID_AA64SMFR0, B16F32, 34, 1) FIELD(ID_AA64SMFR0, F16F32, 35, 1) FIELD(ID_AA64SMFR0, I8I32, 36, 4) +FIELD(ID_AA64SMFR0, F16F16, 42, 1) +FIELD(ID_AA64SMFR0, B16B16, 43, 1) +FIELD(ID_AA64SMFR0, I16I32, 44, 4) FIELD(ID_AA64SMFR0, F64F64, 48, 1) FIELD(ID_AA64SMFR0, I16I64, 52, 4) FIELD(ID_AA64SMFR0, SMEVER, 56, 4) From 5f7b71fb99dc98831d9ad077fe1a58a4b119e952 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:37:00 +0100 Subject: [PATCH 11/80] target/arm: Update user-mode ID reg mask values For user-only mode we reveal a subset of the AArch64 ID registers to the guest, to emulate the kernel's trap-and-emulate-ID-regs handling. Update the feature bit masks to match upstream kernel commit a48fa7efaf1161c1c. None of these features are yet implemented by QEMU, so this doesn't yet have a behavioural change, but implementation of FEAT_MOPS and FEAT_HBC is imminent. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson --- target/arm/helper.c | 11 ++++++++++- tests/tcg/aarch64/sysregs.c | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 3b22596eab..594985d7c8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8621,11 +8621,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) R_ID_AA64ZFR0_F64MM_MASK }, { .name = "ID_AA64SMFR0_EL1", .exported_bits = R_ID_AA64SMFR0_F32F32_MASK | + R_ID_AA64SMFR0_BI32I32_MASK | R_ID_AA64SMFR0_B16F32_MASK | R_ID_AA64SMFR0_F16F32_MASK | R_ID_AA64SMFR0_I8I32_MASK | + R_ID_AA64SMFR0_F16F16_MASK | + R_ID_AA64SMFR0_B16B16_MASK | + R_ID_AA64SMFR0_I16I32_MASK | R_ID_AA64SMFR0_F64F64_MASK | R_ID_AA64SMFR0_I16I64_MASK | + R_ID_AA64SMFR0_SMEVER_MASK | R_ID_AA64SMFR0_FA64_MASK }, { .name = "ID_AA64MMFR0_EL1", .exported_bits = R_ID_AA64MMFR0_ECV_MASK, @@ -8676,7 +8681,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .exported_bits = R_ID_AA64ISAR2_WFXT_MASK | R_ID_AA64ISAR2_RPRES_MASK | R_ID_AA64ISAR2_GPA3_MASK | - R_ID_AA64ISAR2_APA3_MASK }, + R_ID_AA64ISAR2_APA3_MASK | + R_ID_AA64ISAR2_MOPS_MASK | + R_ID_AA64ISAR2_BC_MASK | + R_ID_AA64ISAR2_RPRFM_MASK | + R_ID_AA64ISAR2_CSSC_MASK }, { .name = "ID_AA64ISAR*_EL1_RESERVED", .is_glob = true }, }; diff --git a/tests/tcg/aarch64/sysregs.c b/tests/tcg/aarch64/sysregs.c index d8eb06abcf..f7a055f1d5 100644 --- a/tests/tcg/aarch64/sysregs.c +++ b/tests/tcg/aarch64/sysregs.c @@ -126,7 +126,7 @@ int main(void) */ get_cpu_reg_check_mask(id_aa64isar0_el1, _m(f0ff,ffff,f0ff,fff0)); get_cpu_reg_check_mask(id_aa64isar1_el1, _m(00ff,f0ff,ffff,ffff)); - get_cpu_reg_check_mask(SYS_ID_AA64ISAR2_EL1, _m(0000,0000,0000,ffff)); + get_cpu_reg_check_mask(SYS_ID_AA64ISAR2_EL1, _m(00ff,0000,00ff,ffff)); /* TGran4 & TGran64 as pegged to -1 */ get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(f000,0000,ff00,0000)); get_cpu_reg_check_mask(id_aa64mmfr1_el1, _m(0000,f000,0000,0000)); @@ -138,7 +138,7 @@ int main(void) get_cpu_reg_check_mask(id_aa64dfr0_el1, _m(0000,0000,0000,0006)); get_cpu_reg_check_zero(id_aa64dfr1_el1); get_cpu_reg_check_mask(SYS_ID_AA64ZFR0_EL1, _m(0ff0,ff0f,00ff,00ff)); - get_cpu_reg_check_mask(SYS_ID_AA64SMFR0_EL1, _m(80f1,00fd,0000,0000)); + get_cpu_reg_check_mask(SYS_ID_AA64SMFR0_EL1, _m(8ff1,fcff,0000,0000)); get_cpu_reg_check_zero(id_aa64afr0_el1); get_cpu_reg_check_zero(id_aa64afr1_el1); From 3039b090f2058949edf6a7f1c8e793bc309fa6de Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 15 Sep 2023 15:37:00 +0100 Subject: [PATCH 12/80] target/arm: Implement FEAT_HBC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FEAT_HBC (Hinted conditional branches) provides a new instruction BC.cond, which behaves exactly like the existing B.cond except that it provides a hint to the branch predictor about the likely behaviour of the branch. Since QEMU does not implement branch prediction, we can treat this identically to B.cond. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson --- docs/system/arm/emulation.rst | 1 + linux-user/elfload.c | 1 + target/arm/cpu.h | 5 +++++ target/arm/tcg/a64.decode | 3 ++- target/arm/tcg/cpu64.c | 4 ++++ target/arm/tcg/translate-a64.c | 4 ++++ 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 3df936fc35..1fb6a2e8c3 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -42,6 +42,7 @@ the following architecture extensions: - FEAT_FlagM2 (Enhancements to flag manipulation instructions) - FEAT_GTG (Guest translation granule size) - FEAT_HAFDBS (Hardware management of the access flag and dirty bit state) +- FEAT_HBC (Hinted conditional branches) - FEAT_HCX (Support for the HCRX_EL2 register) - FEAT_HPDS (Hierarchical permission disables) - FEAT_HPDS2 (Translation table page-based hardware attributes) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index bbb4f08109..203a2b790d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -815,6 +815,7 @@ uint32_t get_elf_hwcap2(void) GET_FEATURE_ID(aa64_sme_f64f64, ARM_HWCAP2_A64_SME_F64F64); GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); + GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); return hwcaps; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 7ba2402f72..bc7a69a875 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -4088,6 +4088,11 @@ static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; } +static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; +} + static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) { return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index ef64a3f9cb..7111317302 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -126,7 +126,8 @@ CBZ sf:1 011010 nz:1 ................... rt:5 &cbz imm=%imm19 TBZ . 011011 nz:1 ..... .............. rt:5 &tbz imm=%imm14 bitpos=%imm31_19 -B_cond 0101010 0 ................... 0 cond:4 imm=%imm19 +# B.cond and BC.cond +B_cond 0101010 0 ................... c:1 cond:4 imm=%imm19 BR 1101011 0000 11111 000000 rn:5 00000 &r BLR 1101011 0001 11111 000000 rn:5 00000 &r diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 7264ab5ead..57abaea00c 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1027,6 +1027,10 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ cpu->isar.id_aa64isar1 = t; + t = cpu->isar.id_aa64isar2; + t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ + cpu->isar.id_aa64isar2 = t; + t = cpu->isar.id_aa64pfr0; t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* FEAT_FP16 */ t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* FEAT_FP16 */ diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1b6fbb61e2..1dd86edae1 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1453,6 +1453,10 @@ static bool trans_TBZ(DisasContext *s, arg_tbz *a) static bool trans_B_cond(DisasContext *s, arg_B_cond *a) { + /* BC.cond is only present with FEAT_HBC */ + if (a->c && !dc_isar_feature(aa64_hbc, s)) { + return false; + } reset_btype(s); if (a->cond < 0x0e) { /* genuinely conditional branches */ From 0b5ad31d2a997c9b80e7e24aafce7f079fc67bbd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 7 Sep 2023 17:03:27 +0100 Subject: [PATCH 13/80] target/arm: Remove unused allocation_tag_mem() argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The allocation_tag_mem() function takes an argument tag_size, but it never uses it. Remove the argument. In mte_probe_int() in particular this also lets us delete the code computing the value we were passing in. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé --- target/arm/tcg/mte_helper.c | 42 +++++++++++++------------------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index b23d11563a..e2494f73cf 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -57,7 +57,6 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) * @ptr_access: the access to use for the virtual address * @ptr_size: the number of bytes in the normal memory access * @tag_access: the access to use for the tag memory - * @tag_size: the number of bytes in the tag memory access * @ra: the return address for exception handling * * Our tag memory is formatted as a sequence of little-endian nibbles. @@ -69,15 +68,12 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) * a pointer to the corresponding tag byte. Exit with exception if the * virtual address is not accessible for @ptr_access. * - * The @ptr_size and @tag_size values may not have an obvious relation - * due to the alignment of @ptr, and the number of tag checks required. - * * If there is no tag storage corresponding to @ptr, return NULL. */ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, uint64_t ptr, MMUAccessType ptr_access, int ptr_size, MMUAccessType tag_access, - int tag_size, uintptr_t ra) + uintptr_t ra) { #ifdef CONFIG_USER_ONLY uint64_t clean_ptr = useronly_clean_ptr(ptr); @@ -275,7 +271,7 @@ uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt) /* Trap if accessing an invalid page. */ mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, 1, - MMU_DATA_LOAD, 1, GETPC()); + MMU_DATA_LOAD, GETPC()); /* Load if page supports tags. */ if (mem) { @@ -329,7 +325,7 @@ static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt, /* Trap if accessing an invalid page. */ mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, TAG_GRANULE, - MMU_DATA_STORE, 1, ra); + MMU_DATA_STORE, ra); /* Store if page supports tags. */ if (mem) { @@ -372,10 +368,10 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, if (ptr & TAG_GRANULE) { /* Two stores unaligned mod TAG_GRANULE*2 -- modify two bytes. */ mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - TAG_GRANULE, MMU_DATA_STORE, 1, ra); + TAG_GRANULE, MMU_DATA_STORE, ra); mem2 = allocation_tag_mem(env, mmu_idx, ptr + TAG_GRANULE, MMU_DATA_STORE, TAG_GRANULE, - MMU_DATA_STORE, 1, ra); + MMU_DATA_STORE, ra); /* Store if page(s) support tags. */ if (mem1) { @@ -387,7 +383,7 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, } else { /* Two stores aligned mod TAG_GRANULE*2 -- modify one byte. */ mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - 2 * TAG_GRANULE, MMU_DATA_STORE, 1, ra); + 2 * TAG_GRANULE, MMU_DATA_STORE, ra); if (mem1) { tag |= tag << 4; qatomic_set(mem1, tag); @@ -435,8 +431,7 @@ uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr) /* Trap if accessing an invalid page. */ tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, - gm_bs_bytes, MMU_DATA_LOAD, - gm_bs_bytes / (2 * TAG_GRANULE), ra); + gm_bs_bytes, MMU_DATA_LOAD, ra); /* The tag is squashed to zero if the page does not support tags. */ if (!tag_mem) { @@ -495,8 +490,7 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val) /* Trap if accessing an invalid page. */ tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - gm_bs_bytes, MMU_DATA_LOAD, - gm_bs_bytes / (2 * TAG_GRANULE), ra); + gm_bs_bytes, MMU_DATA_LOAD, ra); /* * Tag store only happens if the page support tags, @@ -552,7 +546,7 @@ void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val) ptr &= -dcz_bytes; mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, dcz_bytes, - MMU_DATA_STORE, tag_bytes, ra); + MMU_DATA_STORE, ra); if (mem) { int tag_pair = (val & 0xf) * 0x11; memset(mem, tag_pair, tag_bytes); @@ -732,8 +726,7 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, int mmu_idx, ptr_tag, bit55; uint64_t ptr_last, prev_page, next_page; uint64_t tag_first, tag_last; - uint64_t tag_byte_first, tag_byte_last; - uint32_t sizem1, tag_count, tag_size, n, c; + uint32_t sizem1, tag_count, n, c; uint8_t *mem1, *mem2; MMUAccessType type; @@ -763,19 +756,14 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, tag_last = QEMU_ALIGN_DOWN(ptr_last, TAG_GRANULE); tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; - /* Round the bounds to twice the tag granule, and compute the bytes. */ - tag_byte_first = QEMU_ALIGN_DOWN(ptr, 2 * TAG_GRANULE); - tag_byte_last = QEMU_ALIGN_DOWN(ptr_last, 2 * TAG_GRANULE); - /* Locate the page boundaries. */ prev_page = ptr & TARGET_PAGE_MASK; next_page = prev_page + TARGET_PAGE_SIZE; if (likely(tag_last - prev_page < TARGET_PAGE_SIZE)) { /* Memory access stays on one page. */ - tag_size = ((tag_byte_last - tag_byte_first) / (2 * TAG_GRANULE)) + 1; mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, sizem1 + 1, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); if (!mem1) { return 1; } @@ -783,14 +771,12 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, tag_count); } else { /* Memory access crosses to next page. */ - tag_size = (next_page - tag_byte_first) / (2 * TAG_GRANULE); mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, next_page - ptr, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); - tag_size = ((tag_byte_last - next_page) / (2 * TAG_GRANULE)) + 1; mem2 = allocation_tag_mem(env, mmu_idx, next_page, type, ptr_last - next_page + 1, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); /* * Perform all of the comparisons. @@ -918,7 +904,7 @@ uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); (void) probe_write(env, ptr, 1, mmu_idx, ra); mem = allocation_tag_mem(env, mmu_idx, align_ptr, MMU_DATA_STORE, - dcz_bytes, MMU_DATA_LOAD, tag_bytes, ra); + dcz_bytes, MMU_DATA_LOAD, ra); if (!mem) { goto done; } From 903dbefc2b6918c10d12d9aafa0168cee8d287c7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:23 +0100 Subject: [PATCH 14/80] target/arm: Don't skip MTE checks for LDRT/STRT at EL0 The LDRT/STRT "unprivileged load/store" instructions behave like normal ones if executed at EL0. We handle this correctly for the load/store semantics, but get the MTE checking wrong. We always look at s->mte_active[is_unpriv] to see whether we should be doing MTE checks, but in hflags.c when we set the TB flags that will be used to fill the mte_active[] array we only set the MTE0_ACTIVE bit if UNPRIV is true (i.e. we are not at EL0). This means that a LDRT at EL0 will see s->mte_active[1] as 0, and will not do MTE checks even when MTE is enabled. To avoid the translate-time code having to do an explicit check on s->unpriv to see if it is OK to index into the mte_active[] array, duplicate MTE_ACTIVE into MTE0_ACTIVE when UNPRIV is false. (This isn't a very serious bug because generally nobody executes LDRT/STRT at EL0, because they have no use there.) Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-2-peter.maydell@linaro.org --- target/arm/tcg/hflags.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 616c5fa723..ea642384f5 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -306,6 +306,15 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, && !(env->pstate & PSTATE_TCO) && (sctlr & (el == 0 ? SCTLR_TCF0 : SCTLR_TCF))) { DP_TBFLAG_A64(flags, MTE_ACTIVE, 1); + if (!EX_TBFLAG_A64(flags, UNPRIV)) { + /* + * In non-unpriv contexts (eg EL0), unpriv load/stores + * act like normal ones; duplicate the MTE info to + * avoid translate-a64.c having to check UNPRIV to see + * whether it is OK to index into MTE_ACTIVE[]. + */ + DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1); + } } } /* And again for unprivileged accesses, if required. */ From dbc678f90a1dab0d2701b068dd7eab627869d045 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:24 +0100 Subject: [PATCH 15/80] target/arm: Implement FEAT_MOPS enable bits FEAT_MOPS defines a handful of new enable bits: * HCRX_EL2.MSCEn, SCTLR_EL1.MSCEn, SCTLR_EL2.MSCen: define whether the new insns should UNDEF or not * HCRX_EL2.MCE2: defines whether memops exceptions from EL1 should be taken to EL1 or EL2 Since we don't sanitise what bits can be written for the SCTLR registers, we only need to handle the new bits in HCRX_EL2, and define SCTLR_MSCEN for the new SCTLR bit value. The precedence of "HCRX bits acts as 0 if SCR_EL3.HXEn is 0" versus "bit acts as 1 if EL2 disabled" is not clear from the register definition text, but it is clear in the CheckMOPSEnabled() pseudocode(), so we follow that. We'll have to check whether other bits we need to implement in future follow the same logic or not. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-3-peter.maydell@linaro.org --- target/arm/cpu.h | 6 ++++++ target/arm/helper.c | 28 +++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index bc7a69a875..266c1a9ea1 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1315,6 +1315,7 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_EnIB (1U << 30) /* v8.3, AArch64 only */ #define SCTLR_EnIA (1U << 31) /* v8.3, AArch64 only */ #define SCTLR_DSSBS_32 (1U << 31) /* v8.5, AArch32 only */ +#define SCTLR_MSCEN (1ULL << 33) /* FEAT_MOPS */ #define SCTLR_BT0 (1ULL << 35) /* v8.5-BTI */ #define SCTLR_BT1 (1ULL << 36) /* v8.5-BTI */ #define SCTLR_ITFSB (1ULL << 37) /* v8.5-MemTag */ @@ -4281,6 +4282,11 @@ static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; } +static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); +} + /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 594985d7c8..83620787b4 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5980,7 +5980,10 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t valid_mask = 0; - /* No features adding bits to HCRX are implemented. */ + /* FEAT_MOPS adds MSCEn and MCE2 */ + if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { + valid_mask |= HCRX_MSCEN | HCRX_MCE2; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; @@ -6009,13 +6012,24 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) { /* * The bits in this register behave as 0 for all purposes other than - * direct reads of the register if: - * - EL2 is not enabled in the current security state, - * - SCR_EL3.HXEn is 0. + * direct reads of the register if SCR_EL3.HXEn is 0. + * If EL2 is not enabled in the current security state, then the + * bit may behave as if 0, or as if 1, depending on the bit. + * For the moment, we treat the EL2-disabled case as taking + * priority over the HXEn-disabled case. This is true for the only + * bit for a feature which we implement where the answer is different + * for the two cases (MSCEn for FEAT_MOPS). + * This may need to be revisited for future bits. */ - if (!arm_is_el2_enabled(env) - || (arm_feature(env, ARM_FEATURE_EL3) - && !(env->cp15.scr_el3 & SCR_HXEN))) { + if (!arm_is_el2_enabled(env)) { + uint64_t hcrx = 0; + if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { + /* MSCEn behaves as 1 if EL2 is not enabled */ + hcrx |= HCRX_MSCEN; + } + return hcrx; + } + if (arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { return 0; } return env->cp15.hcrx_el2; From 81466e4bad85b99493adb4535b16e8733ac1b72e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:25 +0100 Subject: [PATCH 16/80] target/arm: Pass unpriv bool to get_a64_user_mem_index() In every place that we call the get_a64_user_mem_index() function we do it like this: memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); Refactor so the caller passes in the bool that says whether they want the 'unpriv' or 'normal' mem_index rather than having to do the ?: themselves. Signed-off-by: Peter Maydell Message-id: 20230912140434.1333369-4-peter.maydell@linaro.org --- target/arm/tcg/translate-a64.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1dd86edae1..24afd92914 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -105,9 +105,17 @@ void a64_translate_init(void) } /* - * Return the core mmu_idx to use for A64 "unprivileged load/store" insns + * Return the core mmu_idx to use for A64 load/store insns which + * have a "unprivileged load/store" variant. Those insns access + * EL0 if executed from an EL which has control over EL0 (usually + * EL1) but behave like normal loads and stores if executed from + * elsewhere (eg EL3). + * + * @unpriv : true for the unprivileged encoding; false for the + * normal encoding (in which case we will return the same + * thing as get_mem_index(). */ -static int get_a64_user_mem_index(DisasContext *s) +static int get_a64_user_mem_index(DisasContext *s, bool unpriv) { /* * If AccType_UNPRIV is not used, the insn uses AccType_NORMAL, @@ -115,7 +123,7 @@ static int get_a64_user_mem_index(DisasContext *s) */ ARMMMUIdx useridx = s->mmu_idx; - if (s->unpriv) { + if (unpriv && s->unpriv) { /* * We have pre-computed the condition for AccType_UNPRIV. * Therefore we should never get here with a mmu_idx for @@ -3088,7 +3096,7 @@ static void op_addr_ldst_imm_pre(DisasContext *s, arg_ldst_imm *a, if (!a->p) { tcg_gen_addi_i64(*dirty_addr, *dirty_addr, offset); } - memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + memidx = get_a64_user_mem_index(s, a->unpriv); *clean_addr = gen_mte_check1_mmuidx(s, *dirty_addr, is_store, a->w || a->rn != 31, mop, a->unpriv, memidx); @@ -3109,7 +3117,7 @@ static bool trans_STR_i(DisasContext *s, arg_ldst_imm *a) { bool iss_sf, iss_valid = !a->w; TCGv_i64 clean_addr, dirty_addr, tcg_rt; - int memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + int memidx = get_a64_user_mem_index(s, a->unpriv); MemOp mop = finalize_memop(s, a->sz + a->sign * MO_SIGN); op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, true, mop); @@ -3127,7 +3135,7 @@ static bool trans_LDR_i(DisasContext *s, arg_ldst_imm *a) { bool iss_sf, iss_valid = !a->w; TCGv_i64 clean_addr, dirty_addr, tcg_rt; - int memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + int memidx = get_a64_user_mem_index(s, a->unpriv); MemOp mop = finalize_memop(s, a->sz + a->sign * MO_SIGN); op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, false, mop); From 31aaaddecb36c17eeeb991e2124de5132df18af9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:26 +0100 Subject: [PATCH 17/80] target/arm: Define syndrome function for MOPS exceptions The FEAT_MOPS memory operations can raise a Memory Copy or Memory Set exception if a copy or set instruction is executed when the CPU register state is not correct for that instruction. Define the usual syn_* function that constructs the syndrome register value for these exceptions. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-5-peter.maydell@linaro.org --- target/arm/syndrome.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 8a6b8f8162..5d34755508 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -58,6 +58,7 @@ enum arm_exception_class { EC_DATAABORT = 0x24, EC_DATAABORT_SAME_EL = 0x25, EC_SPALIGNMENT = 0x26, + EC_MOP = 0x27, EC_AA32_FPTRAP = 0x28, EC_AA64_FPTRAP = 0x2c, EC_SERROR = 0x2f, @@ -334,4 +335,15 @@ static inline uint32_t syn_serror(uint32_t extra) return (EC_SERROR << ARM_EL_EC_SHIFT) | ARM_EL_IL | extra; } +static inline uint32_t syn_mop(bool is_set, bool is_setg, int options, + bool epilogue, bool wrong_option, bool option_a, + int destreg, int srcreg, int sizereg) +{ + return (EC_MOP << ARM_EL_EC_SHIFT) | ARM_EL_IL | + (is_set << 24) | (is_setg << 23) | (options << 19) | + (epilogue << 18) | (wrong_option << 17) | (option_a << 16) | + (destreg << 10) | (srcreg << 5) | sizereg; +} + + #endif /* TARGET_ARM_SYNDROME_H */ From aa03378bccb1138cb6a3d5a8c91b11feda036188 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:27 +0100 Subject: [PATCH 18/80] target/arm: New function allocation_tag_mem_probe() For the FEAT_MOPS operations, the existing allocation_tag_mem() function almost does what we want, but it will take a watchpoint exception even for an ra == 0 probe request, and it requires that the caller guarantee that the memory is accessible. For FEAT_MOPS we want a function that will not take any kind of exception, and will return NULL for the not-accessible case. Rename allocation_tag_mem() to allocation_tag_mem_probe() and add an extra 'probe' argument that lets us distinguish these cases; allocation_tag_mem() is now a wrapper that always passes 'false'. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-6-peter.maydell@linaro.org --- target/arm/tcg/mte_helper.c | 48 ++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index e2494f73cf..303bcc7fd8 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -50,13 +50,14 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) } /** - * allocation_tag_mem: + * allocation_tag_mem_probe: * @env: the cpu environment * @ptr_mmu_idx: the addressing regime to use for the virtual address * @ptr: the virtual address for which to look up tag memory * @ptr_access: the access to use for the virtual address * @ptr_size: the number of bytes in the normal memory access * @tag_access: the access to use for the tag memory + * @probe: true to merely probe, never taking an exception * @ra: the return address for exception handling * * Our tag memory is formatted as a sequence of little-endian nibbles. @@ -65,15 +66,25 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) * for the higher addr. * * Here, resolve the physical address from the virtual address, and return - * a pointer to the corresponding tag byte. Exit with exception if the - * virtual address is not accessible for @ptr_access. + * a pointer to the corresponding tag byte. * * If there is no tag storage corresponding to @ptr, return NULL. + * + * If the page is inaccessible for @ptr_access, or has a watchpoint, there are + * three options: + * (1) probe = true, ra = 0 : pure probe -- we return NULL if the page is not + * accessible, and do not take watchpoint traps. The calling code must + * handle those cases in the right priority compared to MTE traps. + * (2) probe = false, ra = 0 : probe, no fault expected -- the caller guarantees + * that the page is going to be accessible. We will take watchpoint traps. + * (3) probe = false, ra != 0 : non-probe -- we will take both memory access + * traps and watchpoint traps. + * (probe = true, ra != 0 is invalid and will assert.) */ -static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, - uint64_t ptr, MMUAccessType ptr_access, - int ptr_size, MMUAccessType tag_access, - uintptr_t ra) +static uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, + uint64_t ptr, MMUAccessType ptr_access, + int ptr_size, MMUAccessType tag_access, + bool probe, uintptr_t ra) { #ifdef CONFIG_USER_ONLY uint64_t clean_ptr = useronly_clean_ptr(ptr); @@ -81,6 +92,8 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, uint8_t *tags; uintptr_t index; + assert(!(probe && ra)); + if (!(flags & (ptr_access == MMU_DATA_STORE ? PAGE_WRITE_ORG : PAGE_READ))) { cpu_loop_exit_sigsegv(env_cpu(env), ptr, ptr_access, !(flags & PAGE_VALID), ra); @@ -111,12 +124,16 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, * exception for inaccessible pages, and resolves the virtual address * into the softmmu tlb. * - * When RA == 0, this is for mte_probe. The page is expected to be - * valid. Indicate to probe_access_flags no-fault, then assert that - * we received a valid page. + * When RA == 0, this is either a pure probe or a no-fault-expected probe. + * Indicate to probe_access_flags no-fault, then either return NULL + * for the pure probe, or assert that we received a valid page for the + * no-fault-expected probe. */ flags = probe_access_full(env, ptr, 0, ptr_access, ptr_mmu_idx, ra == 0, &host, &full, ra); + if (probe && (flags & TLB_INVALID_MASK)) { + return NULL; + } assert(!(flags & TLB_INVALID_MASK)); /* If the virtual page MemAttr != Tagged, access unchecked. */ @@ -157,7 +174,7 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, } /* Any debug exception has priority over a tag check exception. */ - if (unlikely(flags & TLB_WATCHPOINT)) { + if (!probe && unlikely(flags & TLB_WATCHPOINT)) { int wp = ptr_access == MMU_DATA_LOAD ? BP_MEM_READ : BP_MEM_WRITE; assert(ra != 0); cpu_check_watchpoint(env_cpu(env), ptr, ptr_size, attrs, wp, ra); @@ -199,6 +216,15 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, #endif } +static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, + uint64_t ptr, MMUAccessType ptr_access, + int ptr_size, MMUAccessType tag_access, + uintptr_t ra) +{ + return allocation_tag_mem_probe(env, ptr_mmu_idx, ptr, ptr_access, + ptr_size, tag_access, false, ra); +} + uint64_t HELPER(irg)(CPUARMState *env, uint64_t rn, uint64_t rm) { uint16_t exclude = extract32(rm | env->cp15.gcr_el1, 0, 16); From 8163998920388d7fa8534185460320838046f089 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:28 +0100 Subject: [PATCH 19/80] target/arm: Implement MTE tag-checking functions for FEAT_MOPS The FEAT_MOPS instructions need a couple of helper routines that check for MTE tag failures: * mte_mops_probe() checks whether there is going to be a tag error in the next up-to-a-page worth of data * mte_check_fail() is an existing function to record the fact of a tag failure, which we need to make global so we can call it from helper-a64.c Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-7-peter.maydell@linaro.org --- target/arm/internals.h | 28 +++++++++++++++++++ target/arm/tcg/mte_helper.c | 54 +++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 5f5393b25c..a70a7fd50f 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1272,6 +1272,34 @@ FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - 12) /* size - 1 */ bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr); uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra); +/** + * mte_mops_probe: Check where the next MTE failure is for a FEAT_MOPS operation + * @env: CPU env + * @ptr: start address of memory region (dirty pointer) + * @size: length of region (guaranteed not to cross a page boundary) + * @desc: MTEDESC descriptor word (0 means no MTE checks) + * Returns: the size of the region that can be copied without hitting + * an MTE tag failure + * + * Note that we assume that the caller has already checked the TBI + * and TCMA bits with mte_checks_needed() and an MTE check is definitely + * required. + */ +uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc); + +/** + * mte_check_fail: Record an MTE tag check failure + * @env: CPU env + * @desc: MTEDESC descriptor word + * @dirty_ptr: Failing dirty address + * @ra: TCG retaddr + * + * This may never return (if the MTE tag checks are configured to fault). + */ +void mte_check_fail(CPUARMState *env, uint32_t desc, + uint64_t dirty_ptr, uintptr_t ra); + static inline int allocation_tag_from_addr(uint64_t ptr) { return extract64(ptr, 56, 4); diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 303bcc7fd8..1cb61cea7a 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -617,8 +617,8 @@ static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr, } /* Record a tag check failure. */ -static void mte_check_fail(CPUARMState *env, uint32_t desc, - uint64_t dirty_ptr, uintptr_t ra) +void mte_check_fail(CPUARMState *env, uint32_t desc, + uint64_t dirty_ptr, uintptr_t ra) { int mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); ARMMMUIdx arm_mmu_idx = core_to_aa64_mmu_idx(mmu_idx); @@ -991,3 +991,53 @@ uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) done: return useronly_clean_ptr(ptr); } + +uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag, tag_first, tag_last; + void *mem; + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + uint32_t n; + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* True probe; this will never fault */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, + w ? MMU_DATA_STORE : MMU_DATA_LOAD, + size, MMU_DATA_LOAD, true, 0); + if (!mem) { + return size; + } + + /* + * TODO: checkN() is not designed for checks of the size we expect + * for FEAT_MOPS operations, so we should implement this differently. + * Maybe we should do something like + * if (region start and size are aligned nicely) { + * do direct loads of 64 tag bits at a time; + * } else { + * call checkN() + * } + */ + /* Round the bounds to the tag granule, and compute the number of tags. */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); + tag_last = QEMU_ALIGN_DOWN(ptr + size - 1, TAG_GRANULE); + tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; + n = checkN(mem, ptr & TAG_GRANULE, ptr_tag, tag_count); + if (likely(n == tag_count)) { + return size; + } + + /* + * Failure; for the first granule, it's at @ptr. Otherwise + * it's at the first byte of the nth granule. Calculate how + * many bytes we can access without hitting that failure. + */ + if (n == 0) { + return 0; + } else { + return n * TAG_GRANULE - (ptr - tag_first); + } +} From 0e92818887deef2300255300af57bb58a6af1ad4 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:29 +0100 Subject: [PATCH 20/80] target/arm: Implement the SET* instructions Implement the SET* instructions which collectively implement a "memset" operation. These come in a set of three, eg SETP (prologue), SETM (main), SETE (epilogue), and each of those has different flavours to indicate whether memory accesses should be unpriv or non-temporal. This commit does not include the "memset with tag setting" SETG* instructions. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-8-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 16 ++ target/arm/tcg/helper-a64.c | 344 +++++++++++++++++++++++++++++++++ target/arm/tcg/helper-a64.h | 4 + target/arm/tcg/translate-a64.c | 49 +++++ 4 files changed, 413 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 7111317302..c2a97328ee 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -554,3 +554,19 @@ LDGM 11011001 11 1 ......... 00 ..... ..... @ldst_tag_mult p=0 w=0 STZ2G 11011001 11 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 STZ2G 11011001 11 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 STZ2G 11011001 11 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +# Memory operations (memset, memcpy, memmove) +# Each of these comes in a set of three, eg SETP (prologue), SETM (main), +# SETE (epilogue), and each of those has different flavours to +# indicate whether memory accesses should be unpriv or non-temporal. +# We don't distinguish temporal and non-temporal accesses, but we +# do need to report it in syndrome register values. + +# Memset +&set rs rn rd unpriv nontemp +# op2 bit 1 is nontemporal bit +@set .. ......... rs:5 .. nontemp:1 unpriv:1 .. rn:5 rd:5 &set + +SETP 00 011001110 ..... 00 . . 01 ..... ..... @set +SETM 00 011001110 ..... 01 . . 01 ..... ..... @set +SETE 00 011001110 ..... 10 . . 01 ..... ..... @set diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 0cf56f6dc4..24ae5ecf32 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -968,3 +968,347 @@ void HELPER(unaligned_access)(CPUARMState *env, uint64_t addr, arm_cpu_do_unaligned_access(env_cpu(env), addr, access_type, mmu_idx, GETPC()); } + +/* Memory operations (memset, memmove, memcpy) */ + +/* + * Return true if the CPY* and SET* insns can execute; compare + * pseudocode CheckMOPSEnabled(), though we refactor it a little. + */ +static bool mops_enabled(CPUARMState *env) +{ + int el = arm_current_el(env); + + if (el < 2 && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE) && + !(arm_hcrx_el2_eff(env) & HCRX_MSCEN)) { + return false; + } + + if (el == 0) { + if (!el_is_in_host(env, 0)) { + return env->cp15.sctlr_el[1] & SCTLR_MSCEN; + } else { + return env->cp15.sctlr_el[2] & SCTLR_MSCEN; + } + } + return true; +} + +static void check_mops_enabled(CPUARMState *env, uintptr_t ra) +{ + if (!mops_enabled(env)) { + raise_exception_ra(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env), ra); + } +} + +/* + * Return the target exception level for an exception due + * to mismatched arguments in a FEAT_MOPS copy or set. + * Compare pseudocode MismatchedCpySetTargetEL() + */ +static int mops_mismatch_exception_target_el(CPUARMState *env) +{ + int el = arm_current_el(env); + + if (el > 1) { + return el; + } + if (el == 0 && (arm_hcr_el2_eff(env) & HCR_TGE)) { + return 2; + } + if (el == 1 && (arm_hcrx_el2_eff(env) & HCRX_MCE2)) { + return 2; + } + return 1; +} + +/* + * Check whether an M or E instruction was executed with a CF value + * indicating the wrong option for this implementation. + * Assumes we are always Option A. + */ +static void check_mops_wrong_option(CPUARMState *env, uint32_t syndrome, + uintptr_t ra) +{ + if (env->CF != 0) { + syndrome |= 1 << 17; /* Set the wrong-option bit */ + raise_exception_ra(env, EXCP_UDEF, syndrome, + mops_mismatch_exception_target_el(env), ra); + } +} + +/* + * Return the maximum number of bytes we can transfer starting at addr + * without crossing a page boundary. + */ +static uint64_t page_limit(uint64_t addr) +{ + return TARGET_PAGE_ALIGN(addr + 1) - addr; +} + +/* + * Perform part of a memory set on an area of guest memory starting at + * toaddr (a dirty address) and extending for setsize bytes. + * + * Returns the number of bytes actually set, which might be less than + * setsize; the caller should loop until the whole set has been done. + * The caller should ensure that the guest registers are correct + * for the possibility that the first byte of the set encounters + * an exception or watchpoint. We guarantee not to take any faults + * for bytes other than the first. + */ +static uint64_t set_step(CPUARMState *env, uint64_t toaddr, + uint64_t setsize, uint32_t data, int memidx, + uint32_t *mtedesc, uintptr_t ra) +{ + void *mem; + + setsize = MIN(setsize, page_limit(toaddr)); + if (*mtedesc) { + uint64_t mtesize = mte_mops_probe(env, toaddr, setsize, *mtedesc); + if (mtesize == 0) { + /* Trap, or not. All CPU state is up to date */ + mte_check_fail(env, *mtedesc, toaddr, ra); + /* Continue, with no further MTE checks required */ + *mtedesc = 0; + } else { + /* Advance to the end, or to the tag mismatch */ + setsize = MIN(setsize, mtesize); + } + } + + toaddr = useronly_clean_ptr(toaddr); + /* + * Trapless lookup: returns NULL for invalid page, I/O, + * watchpoints, clean pages, etc. + */ + mem = tlb_vaddr_to_host(env, toaddr, MMU_DATA_STORE, memidx); + +#ifndef CONFIG_USER_ONLY + if (unlikely(!mem)) { + /* + * Slow-path: just do one byte write. This will handle the + * watchpoint, invalid page, etc handling correctly. + * For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + cpu_stb_mmuidx_ra(env, toaddr, data, memidx, ra); + return 1; + } +#endif + /* Easy case: just memset the host memory */ + memset(mem, data, setsize); + return setsize; +} + +typedef uint64_t StepFn(CPUARMState *env, uint64_t toaddr, + uint64_t setsize, uint32_t data, + int memidx, uint32_t *mtedesc, uintptr_t ra); + +/* Extract register numbers from a MOPS exception syndrome value */ +static int mops_destreg(uint32_t syndrome) +{ + return extract32(syndrome, 10, 5); +} + +static int mops_srcreg(uint32_t syndrome) +{ + return extract32(syndrome, 5, 5); +} + +static int mops_sizereg(uint32_t syndrome) +{ + return extract32(syndrome, 0, 5); +} + +/* + * Return true if TCMA and TBI bits mean we need to do MTE checks. + * We only need to do this once per MOPS insn, not for every page. + */ +static bool mte_checks_needed(uint64_t ptr, uint32_t desc) +{ + int bit55 = extract64(ptr, 55, 1); + + /* + * Note that tbi_check() returns true for "access checked" but + * tcma_check() returns true for "access unchecked". + */ + if (!tbi_check(desc, bit55)) { + return false; + } + return !tcma_check(desc, bit55, allocation_tag_from_addr(ptr)); +} + +/* + * For the Memory Set operation, our implementation chooses + * always to use "option A", where we update Xd to the final + * address in the SETP insn, and set Xn to be -(bytes remaining). + * On SETM and SETE insns we only need update Xn. + * + * @env: CPU + * @syndrome: syndrome value for mismatch exceptions + * (also contains the register numbers we need to use) + * @mtedesc: MTE descriptor word + * @stepfn: function which does a single part of the set operation + * @is_setg: true if this is the tag-setting SETG variant + */ +static void do_setp(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, + StepFn *stepfn, bool is_setg, uintptr_t ra) +{ + /* Prologue: we choose to do up to the next page boundary */ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint8_t data = env->xregs[rs]; + uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); + uint64_t toaddr = env->xregs[rd]; + uint64_t setsize = env->xregs[rn]; + uint64_t stagesetsize, step; + + check_mops_enabled(env, ra); + + if (setsize > INT64_MAX) { + setsize = INT64_MAX; + } + + if (!mte_checks_needed(toaddr, mtedesc)) { + mtedesc = 0; + } + + stagesetsize = MIN(setsize, page_limit(toaddr)); + while (stagesetsize) { + env->xregs[rd] = toaddr; + env->xregs[rn] = setsize; + step = stepfn(env, toaddr, stagesetsize, data, memidx, &mtedesc, ra); + toaddr += step; + setsize -= step; + stagesetsize -= step; + } + /* Insn completed, so update registers to the Option A format */ + env->xregs[rd] = toaddr + setsize; + env->xregs[rn] = -setsize; + + /* Set NZCV = 0000 to indicate we are an Option A implementation */ + env->NF = 0; + env->ZF = 1; /* our env->ZF encoding is inverted */ + env->CF = 0; + env->VF = 0; + return; +} + +void HELPER(setp)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setp(env, syndrome, mtedesc, set_step, false, GETPC()); +} + +static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, + StepFn *stepfn, bool is_setg, uintptr_t ra) +{ + /* Main: we choose to do all the full-page chunks */ + CPUState *cs = env_cpu(env); + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint8_t data = env->xregs[rs]; + uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; + uint64_t setsize = -env->xregs[rn]; + uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); + uint64_t step, stagesetsize; + + check_mops_enabled(env, ra); + + /* + * We're allowed to NOP out "no data to copy" before the consistency + * checks; we choose to do so. + */ + if (env->xregs[rn] == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + /* + * Our implementation will work fine even if we have an unaligned + * destination address, and because we update Xn every time around + * the loop below and the return value from stepfn() may be less + * than requested, we might find toaddr is unaligned. So we don't + * have an IMPDEF check for alignment here. + */ + + if (!mte_checks_needed(toaddr, mtedesc)) { + mtedesc = 0; + } + + /* Do the actual memset: we leave the last partial page to SETE */ + stagesetsize = setsize & TARGET_PAGE_MASK; + while (stagesetsize > 0) { + step = stepfn(env, toaddr, setsize, data, memidx, &mtedesc, ra); + toaddr += step; + setsize -= step; + stagesetsize -= step; + env->xregs[rn] = -setsize; + if (stagesetsize > 0 && unlikely(cpu_loop_exit_requested(cs))) { + cpu_loop_exit_restore(cs, ra); + } + } +} + +void HELPER(setm)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setm(env, syndrome, mtedesc, set_step, false, GETPC()); +} + +static void do_sete(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, + StepFn *stepfn, bool is_setg, uintptr_t ra) +{ + /* Epilogue: do the last partial page */ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint8_t data = env->xregs[rs]; + uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; + uint64_t setsize = -env->xregs[rn]; + uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); + uint64_t step; + + check_mops_enabled(env, ra); + + /* + * We're allowed to NOP out "no data to copy" before the consistency + * checks; we choose to do so. + */ + if (setsize == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + /* + * Our implementation has no address alignment requirements, but + * we do want to enforce the "less than a page" size requirement, + * so we don't need to have the "check for interrupts" here. + */ + if (setsize >= TARGET_PAGE_SIZE) { + raise_exception_ra(env, EXCP_UDEF, syndrome, + mops_mismatch_exception_target_el(env), ra); + } + + if (!mte_checks_needed(toaddr, mtedesc)) { + mtedesc = 0; + } + + /* Do the actual memset */ + while (setsize > 0) { + step = stepfn(env, toaddr, setsize, data, memidx, &mtedesc, ra); + toaddr += step; + setsize -= step; + env->xregs[rn] = -setsize; + } +} + +void HELPER(sete)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_sete(env, syndrome, mtedesc, set_step, false, GETPC()); +} diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 57cfd68569..7ce5d2105a 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -117,3 +117,7 @@ DEF_HELPER_FLAGS_3(stzgm_tags, TCG_CALL_NO_WG, void, env, i64, i64) DEF_HELPER_FLAGS_4(unaligned_access, TCG_CALL_NO_WG, noreturn, env, i64, i32, i32) + +DEF_HELPER_3(setp, void, env, i32, i32) +DEF_HELPER_3(setm, void, env, i32, i32) +DEF_HELPER_3(sete, void, env, i32, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 24afd92914..bb7b15cb6c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3962,6 +3962,55 @@ TRANS_FEAT(STZG, aa64_mte_insn_reg, do_STG, a, true, false) TRANS_FEAT(ST2G, aa64_mte_insn_reg, do_STG, a, false, true) TRANS_FEAT(STZ2G, aa64_mte_insn_reg, do_STG, a, true, true) +typedef void SetFn(TCGv_env, TCGv_i32, TCGv_i32); + +static bool do_SET(DisasContext *s, arg_set *a, bool is_epilogue, SetFn fn) +{ + int memidx; + uint32_t syndrome, desc = 0; + + /* + * UNPREDICTABLE cases: we choose to UNDEF, which allows + * us to pull this check before the CheckMOPSEnabled() test + * (which we do in the helper function) + */ + if (a->rs == a->rn || a->rs == a->rd || a->rn == a->rd || + a->rd == 31 || a->rn == 31) { + return false; + } + + memidx = get_a64_user_mem_index(s, a->unpriv); + + /* + * We pass option_a == true, matching our implementation; + * we pass wrong_option == false: helper function may set that bit. + */ + syndrome = syn_mop(true, false, (a->nontemp << 1) | a->unpriv, + is_epilogue, false, true, a->rd, a->rs, a->rn); + + if (s->mte_active[a->unpriv]) { + /* We may need to do MTE tag checking, so assemble the descriptor */ + desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); + desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); + desc = FIELD_DP32(desc, MTEDESC, WRITE, true); + /* SIZEM1 and ALIGN we leave 0 (byte write) */ + } + /* The helper function always needs the memidx even with MTE disabled */ + desc = FIELD_DP32(desc, MTEDESC, MIDX, memidx); + + /* + * The helper needs the register numbers, but since they're in + * the syndrome anyway, we let it extract them from there rather + * than passing in an extra three integer arguments. + */ + fn(cpu_env, tcg_constant_i32(syndrome), tcg_constant_i32(desc)); + return true; +} + +TRANS_FEAT(SETP, aa64_mops, do_SET, a, false, gen_helper_setp) +TRANS_FEAT(SETM, aa64_mops, do_SET, a, false, gen_helper_setm) +TRANS_FEAT(SETE, aa64_mops, do_SET, a, true, gen_helper_sete) + typedef void ArithTwoOp(TCGv_i64, TCGv_i64, TCGv_i64); static bool gen_rri(DisasContext *s, arg_rri_sf *a, From 179e9a3baccc3918846c9fc3de2fd534a9a2f901 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:30 +0100 Subject: [PATCH 21/80] target/arm: Define new TB flag for ATA0 Currently the only tag-setting instructions always do so in the context of the current EL, and so we only need one ATA bit in the TB flags. The FEAT_MOPS SETG instructions include ones which set tags for a non-privileged access, so we now also need the equivalent "are tags enabled?" information for EL0. Add the new TB flag, and convert the existing 'bool ata' field in DisasContext to a 'bool ata[2]' that can be indexed by the is_unpriv bit in an instruction, similarly to mte[2]. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-9-peter.maydell@linaro.org --- target/arm/cpu.h | 1 + target/arm/tcg/hflags.c | 12 ++++++++++++ target/arm/tcg/translate-a64.c | 23 ++++++++++++----------- target/arm/tcg/translate.h | 4 ++-- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 266c1a9ea1..bd55c5dabf 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3171,6 +3171,7 @@ FIELD(TBFLAG_A64, SVL, 24, 4) FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) FIELD(TBFLAG_A64, FGT_ERET, 29, 1) FIELD(TBFLAG_A64, NAA, 30, 1) +FIELD(TBFLAG_A64, ATA0, 31, 1) /* * Helpers for using the above. diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index ea642384f5..cea1adb7b6 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -325,6 +325,18 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, && allocation_tag_access_enabled(env, 0, sctlr)) { DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1); } + /* + * For unpriv tag-setting accesses we alse need ATA0. Again, in + * contexts where unpriv and normal insns are the same we + * duplicate the ATA bit to save effort for translate-a64.c. + */ + if (EX_TBFLAG_A64(flags, UNPRIV)) { + if (allocation_tag_access_enabled(env, 0, sctlr)) { + DP_TBFLAG_A64(flags, ATA0, 1); + } + } else { + DP_TBFLAG_A64(flags, ATA0, EX_TBFLAG_A64(flags, ATA)); + } /* Cache TCMA as well as TBI. */ DP_TBFLAG_A64(flags, TCMA, aa64_va_parameter_tcma(tcr, mmu_idx)); } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index bb7b15cb6c..da4aabbaf4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2272,7 +2272,7 @@ static void handle_sys(DisasContext *s, bool isread, clean_addr = clean_data_tbi(s, tcg_rt); gen_probe_access(s, clean_addr, MMU_DATA_STORE, MO_8); - if (s->ata) { + if (s->ata[0]) { /* Extract the tag from the register to match STZGM. */ tag = tcg_temp_new_i64(); tcg_gen_shri_i64(tag, tcg_rt, 56); @@ -2289,7 +2289,7 @@ static void handle_sys(DisasContext *s, bool isread, clean_addr = clean_data_tbi(s, tcg_rt); gen_helper_dc_zva(cpu_env, clean_addr); - if (s->ata) { + if (s->ata[0]) { /* Extract the tag from the register to match STZGM. */ tag = tcg_temp_new_i64(); tcg_gen_shri_i64(tag, tcg_rt, 56); @@ -3070,7 +3070,7 @@ static bool trans_STGP(DisasContext *s, arg_ldstpair *a) tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); /* Perform the tag store, if tag access enabled. */ - if (s->ata) { + if (s->ata[0]) { if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_stg_parallel(cpu_env, dirty_addr, dirty_addr); } else { @@ -3768,7 +3768,7 @@ static bool trans_STZGM(DisasContext *s, arg_ldst_tag *a) tcg_gen_addi_i64(addr, addr, a->imm); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { + if (s->ata[0]) { gen_helper_stzgm_tags(cpu_env, addr, tcg_rt); } /* @@ -3800,7 +3800,7 @@ static bool trans_STGM(DisasContext *s, arg_ldst_tag *a) tcg_gen_addi_i64(addr, addr, a->imm); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { + if (s->ata[0]) { gen_helper_stgm(cpu_env, addr, tcg_rt); } else { MMUAccessType acc = MMU_DATA_STORE; @@ -3832,7 +3832,7 @@ static bool trans_LDGM(DisasContext *s, arg_ldst_tag *a) tcg_gen_addi_i64(addr, addr, a->imm); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { + if (s->ata[0]) { gen_helper_ldgm(tcg_rt, cpu_env, addr); } else { MMUAccessType acc = MMU_DATA_LOAD; @@ -3867,7 +3867,7 @@ static bool trans_LDG(DisasContext *s, arg_ldst_tag *a) tcg_gen_andi_i64(addr, addr, -TAG_GRANULE); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { + if (s->ata[0]) { gen_helper_ldg(tcg_rt, cpu_env, addr, tcg_rt); } else { /* @@ -3904,7 +3904,7 @@ static bool do_STG(DisasContext *s, arg_ldst_tag *a, bool is_zero, bool is_pair) tcg_gen_addi_i64(addr, addr, a->imm); } tcg_rt = cpu_reg_sp(s, a->rt); - if (!s->ata) { + if (!s->ata[0]) { /* * For STG and ST2G, we need to check alignment and probe memory. * TODO: For STZG and STZ2G, we could rely on the stores below, @@ -4073,7 +4073,7 @@ static bool gen_add_sub_imm_with_tags(DisasContext *s, arg_rri_tag *a, tcg_rn = cpu_reg_sp(s, a->rn); tcg_rd = cpu_reg_sp(s, a->rd); - if (s->ata) { + if (s->ata[0]) { gen_helper_addsubg(tcg_rd, cpu_env, tcg_rn, tcg_constant_i32(imm), tcg_constant_i32(a->uimm4)); @@ -5460,7 +5460,7 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { goto do_unallocated; } - if (s->ata) { + if (s->ata[0]) { gen_helper_irg(cpu_reg_sp(s, rd), cpu_env, cpu_reg_sp(s, rn), cpu_reg(s, rm)); } else { @@ -13951,7 +13951,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->bt = EX_TBFLAG_A64(tb_flags, BT); dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE); dc->unpriv = EX_TBFLAG_A64(tb_flags, UNPRIV); - dc->ata = EX_TBFLAG_A64(tb_flags, ATA); + dc->ata[0] = EX_TBFLAG_A64(tb_flags, ATA); + dc->ata[1] = EX_TBFLAG_A64(tb_flags, ATA0); dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE); dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE); dc->pstate_sm = EX_TBFLAG_A64(tb_flags, PSTATE_SM); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index f748ba6f39..63922f8bad 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -114,8 +114,8 @@ typedef struct DisasContext { bool unpriv; /* True if v8.3-PAuth is active. */ bool pauth_active; - /* True if v8.5-MTE access to tags is enabled. */ - bool ata; + /* True if v8.5-MTE access to tags is enabled; index with is_unpriv. */ + bool ata[2]; /* True if v8.5-MTE tag checks affect the PE; index with is_unpriv. */ bool mte_active[2]; /* True with v8.5-BTI and SCTLR_ELx.BT* set. */ From 6087df574400659226861fa5ba47970f1fbd277b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:31 +0100 Subject: [PATCH 22/80] target/arm: Implement the SETG* instructions The FEAT_MOPS SETG* instructions are very similar to the SET* instructions, but as well as setting memory contents they also set the MTE tags. They are architecturally required to operate on tag-granule aligned regions only. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-10-peter.maydell@linaro.org --- target/arm/internals.h | 10 ++++ target/arm/tcg/a64.decode | 5 ++ target/arm/tcg/helper-a64.c | 86 ++++++++++++++++++++++++++++++++-- target/arm/tcg/helper-a64.h | 3 ++ target/arm/tcg/mte_helper.c | 40 ++++++++++++++++ target/arm/tcg/translate-a64.c | 20 +++++--- 6 files changed, 155 insertions(+), 9 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index a70a7fd50f..642f77df29 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1300,6 +1300,16 @@ uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, void mte_check_fail(CPUARMState *env, uint32_t desc, uint64_t dirty_ptr, uintptr_t ra); +/** + * mte_mops_set_tags: Set MTE tags for a portion of a FEAT_MOPS operation + * @env: CPU env + * @dirty_ptr: Start address of memory region (dirty pointer) + * @size: length of region (guaranteed not to cross page boundary) + * @desc: MTEDESC descriptor word + */ +void mte_mops_set_tags(CPUARMState *env, uint64_t dirty_ptr, uint64_t size, + uint32_t desc); + static inline int allocation_tag_from_addr(uint64_t ptr) { return extract64(ptr, 56, 4); diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c2a97328ee..a202faa17b 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -570,3 +570,8 @@ STZ2G 11011001 11 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 SETP 00 011001110 ..... 00 . . 01 ..... ..... @set SETM 00 011001110 ..... 01 . . 01 ..... ..... @set SETE 00 011001110 ..... 10 . . 01 ..... ..... @set + +# Like SET, but also setting MTE tags +SETGP 00 011101110 ..... 00 . . 01 ..... ..... @set +SETGM 00 011101110 ..... 01 . . 01 ..... ..... @set +SETGE 00 011101110 ..... 10 . . 01 ..... ..... @set diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 24ae5ecf32..2cf89184d7 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -1103,6 +1103,50 @@ static uint64_t set_step(CPUARMState *env, uint64_t toaddr, return setsize; } +/* + * Similar, but setting tags. The architecture requires us to do this + * in 16-byte chunks. SETP accesses are not tag checked; they set + * the tags. + */ +static uint64_t set_step_tags(CPUARMState *env, uint64_t toaddr, + uint64_t setsize, uint32_t data, int memidx, + uint32_t *mtedesc, uintptr_t ra) +{ + void *mem; + uint64_t cleanaddr; + + setsize = MIN(setsize, page_limit(toaddr)); + + cleanaddr = useronly_clean_ptr(toaddr); + /* + * Trapless lookup: returns NULL for invalid page, I/O, + * watchpoints, clean pages, etc. + */ + mem = tlb_vaddr_to_host(env, cleanaddr, MMU_DATA_STORE, memidx); + +#ifndef CONFIG_USER_ONLY + if (unlikely(!mem)) { + /* + * Slow-path: just do one write. This will handle the + * watchpoint, invalid page, etc handling correctly. + * The architecture requires that we do 16 bytes at a time, + * and we know both ptr and size are 16 byte aligned. + * For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + uint64_t repldata = data * 0x0101010101010101ULL; + MemOpIdx oi16 = make_memop_idx(MO_TE | MO_128, memidx); + cpu_st16_mmu(env, toaddr, int128_make128(repldata, repldata), oi16, ra); + mte_mops_set_tags(env, toaddr, 16, *mtedesc); + return 16; + } +#endif + /* Easy case: just memset the host memory */ + memset(mem, data, setsize); + mte_mops_set_tags(env, toaddr, setsize, *mtedesc); + return setsize; +} + typedef uint64_t StepFn(CPUARMState *env, uint64_t toaddr, uint64_t setsize, uint32_t data, int memidx, uint32_t *mtedesc, uintptr_t ra); @@ -1141,6 +1185,18 @@ static bool mte_checks_needed(uint64_t ptr, uint32_t desc) return !tcma_check(desc, bit55, allocation_tag_from_addr(ptr)); } +/* Take an exception if the SETG addr/size are not granule aligned */ +static void check_setg_alignment(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t memidx, uintptr_t ra) +{ + if ((size != 0 && !QEMU_IS_ALIGNED(ptr, TAG_GRANULE)) || + !QEMU_IS_ALIGNED(size, TAG_GRANULE)) { + arm_cpu_do_unaligned_access(env_cpu(env), ptr, MMU_DATA_STORE, + memidx, ra); + + } +} + /* * For the Memory Set operation, our implementation chooses * always to use "option A", where we update Xd to the final @@ -1171,9 +1227,14 @@ static void do_setp(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, if (setsize > INT64_MAX) { setsize = INT64_MAX; + if (is_setg) { + setsize &= ~0xf; + } } - if (!mte_checks_needed(toaddr, mtedesc)) { + if (unlikely(is_setg)) { + check_setg_alignment(env, toaddr, setsize, memidx, ra); + } else if (!mte_checks_needed(toaddr, mtedesc)) { mtedesc = 0; } @@ -1203,6 +1264,11 @@ void HELPER(setp)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) do_setp(env, syndrome, mtedesc, set_step, false, GETPC()); } +void HELPER(setgp)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setp(env, syndrome, mtedesc, set_step_tags, true, GETPC()); +} + static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, StepFn *stepfn, bool is_setg, uintptr_t ra) { @@ -1237,7 +1303,9 @@ static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, * have an IMPDEF check for alignment here. */ - if (!mte_checks_needed(toaddr, mtedesc)) { + if (unlikely(is_setg)) { + check_setg_alignment(env, toaddr, setsize, memidx, ra); + } else if (!mte_checks_needed(toaddr, mtedesc)) { mtedesc = 0; } @@ -1260,6 +1328,11 @@ void HELPER(setm)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) do_setm(env, syndrome, mtedesc, set_step, false, GETPC()); } +void HELPER(setgm)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setm(env, syndrome, mtedesc, set_step_tags, true, GETPC()); +} + static void do_sete(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, StepFn *stepfn, bool is_setg, uintptr_t ra) { @@ -1295,7 +1368,9 @@ static void do_sete(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, mops_mismatch_exception_target_el(env), ra); } - if (!mte_checks_needed(toaddr, mtedesc)) { + if (unlikely(is_setg)) { + check_setg_alignment(env, toaddr, setsize, memidx, ra); + } else if (!mte_checks_needed(toaddr, mtedesc)) { mtedesc = 0; } @@ -1312,3 +1387,8 @@ void HELPER(sete)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) { do_sete(env, syndrome, mtedesc, set_step, false, GETPC()); } + +void HELPER(setge)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_sete(env, syndrome, mtedesc, set_step_tags, true, GETPC()); +} diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 7ce5d2105a..10a9910712 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -121,3 +121,6 @@ DEF_HELPER_FLAGS_4(unaligned_access, TCG_CALL_NO_WG, DEF_HELPER_3(setp, void, env, i32, i32) DEF_HELPER_3(setm, void, env, i32, i32) DEF_HELPER_3(sete, void, env, i32, i32) +DEF_HELPER_3(setgp, void, env, i32, i32) +DEF_HELPER_3(setgm, void, env, i32, i32) +DEF_HELPER_3(setge, void, env, i32, i32) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 1cb61cea7a..66a80eeb95 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -1041,3 +1041,43 @@ uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, return n * TAG_GRANULE - (ptr - tag_first); } } + +void mte_mops_set_tags(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag; + void *mem; + + if (!desc) { + /* Tags not actually enabled */ + return; + } + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* True probe: this will never fault */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, MMU_DATA_STORE, size, + MMU_DATA_STORE, true, 0); + if (!mem) { + return; + } + + /* + * We know that ptr and size are both TAG_GRANULE aligned; store + * the tag from the pointer value into the tag memory. + */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_count = size / TAG_GRANULE; + if (ptr & TAG_GRANULE) { + /* Not 2*TAG_GRANULE-aligned: store tag to first nibble */ + store_tag1_parallel(TAG_GRANULE, mem, ptr_tag); + mem++; + tag_count--; + } + memset(mem, ptr_tag | (ptr_tag << 4), tag_count / 2); + if (tag_count & 1) { + /* Final trailing unaligned nibble */ + mem += tag_count / 2; + store_tag1_parallel(0, mem, ptr_tag); + } +} diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index da4aabbaf4..27bb3039b4 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3964,11 +3964,16 @@ TRANS_FEAT(STZ2G, aa64_mte_insn_reg, do_STG, a, true, true) typedef void SetFn(TCGv_env, TCGv_i32, TCGv_i32); -static bool do_SET(DisasContext *s, arg_set *a, bool is_epilogue, SetFn fn) +static bool do_SET(DisasContext *s, arg_set *a, bool is_epilogue, + bool is_setg, SetFn fn) { int memidx; uint32_t syndrome, desc = 0; + if (is_setg && !dc_isar_feature(aa64_mte, s)) { + return false; + } + /* * UNPREDICTABLE cases: we choose to UNDEF, which allows * us to pull this check before the CheckMOPSEnabled() test @@ -3985,10 +3990,10 @@ static bool do_SET(DisasContext *s, arg_set *a, bool is_epilogue, SetFn fn) * We pass option_a == true, matching our implementation; * we pass wrong_option == false: helper function may set that bit. */ - syndrome = syn_mop(true, false, (a->nontemp << 1) | a->unpriv, + syndrome = syn_mop(true, is_setg, (a->nontemp << 1) | a->unpriv, is_epilogue, false, true, a->rd, a->rs, a->rn); - if (s->mte_active[a->unpriv]) { + if (is_setg ? s->ata[a->unpriv] : s->mte_active[a->unpriv]) { /* We may need to do MTE tag checking, so assemble the descriptor */ desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); @@ -4007,9 +4012,12 @@ static bool do_SET(DisasContext *s, arg_set *a, bool is_epilogue, SetFn fn) return true; } -TRANS_FEAT(SETP, aa64_mops, do_SET, a, false, gen_helper_setp) -TRANS_FEAT(SETM, aa64_mops, do_SET, a, false, gen_helper_setm) -TRANS_FEAT(SETE, aa64_mops, do_SET, a, true, gen_helper_sete) +TRANS_FEAT(SETP, aa64_mops, do_SET, a, false, false, gen_helper_setp) +TRANS_FEAT(SETM, aa64_mops, do_SET, a, false, false, gen_helper_setm) +TRANS_FEAT(SETE, aa64_mops, do_SET, a, true, false, gen_helper_sete) +TRANS_FEAT(SETGP, aa64_mops, do_SET, a, false, true, gen_helper_setgp) +TRANS_FEAT(SETGM, aa64_mops, do_SET, a, false, true, gen_helper_setgm) +TRANS_FEAT(SETGE, aa64_mops, do_SET, a, true, true, gen_helper_setge) typedef void ArithTwoOp(TCGv_i64, TCGv_i64, TCGv_i64); From 69c51dc3723bdcb8d020f812f0d25d17b466d959 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:32 +0100 Subject: [PATCH 23/80] target/arm: Implement MTE tag-checking functions for FEAT_MOPS copies The FEAT_MOPS memory copy operations need an extra helper routine for checking for MTE tag checking failures beyond the ones we already added for memory set operations: * mte_mops_probe_rev() does the same job as mte_mops_probe(), but it checks tags starting at the provided address and working backwards, rather than forwards Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-11-peter.maydell@linaro.org --- target/arm/internals.h | 17 +++++++ target/arm/tcg/mte_helper.c | 99 +++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/target/arm/internals.h b/target/arm/internals.h index 642f77df29..1dd9182a54 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1288,6 +1288,23 @@ uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra); uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, uint32_t desc); +/** + * mte_mops_probe_rev: Check where the next MTE failure is for a FEAT_MOPS + * operation going in the reverse direction + * @env: CPU env + * @ptr: *end* address of memory region (dirty pointer) + * @size: length of region (guaranteed not to cross a page boundary) + * @desc: MTEDESC descriptor word (0 means no MTE checks) + * Returns: the size of the region that can be copied without hitting + * an MTE tag failure + * + * Note that we assume that the caller has already checked the TBI + * and TCMA bits with mte_checks_needed() and an MTE check is definitely + * required. + */ +uint64_t mte_mops_probe_rev(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc); + /** * mte_check_fail: Record an MTE tag check failure * @env: CPU env diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 66a80eeb95..2dd7eb3edb 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -734,6 +734,55 @@ static int checkN(uint8_t *mem, int odd, int cmp, int count) return n; } +/** + * checkNrev: + * @tag: tag memory to test + * @odd: true to begin testing at tags at odd nibble + * @cmp: the tag to compare against + * @count: number of tags to test + * + * Return the number of successful tests. + * Thus a return value < @count indicates a failure. + * + * This is like checkN, but it runs backwards, checking the + * tags starting with @tag and then the tags preceding it. + * This is needed by the backwards-memory-copying operations. + */ +static int checkNrev(uint8_t *mem, int odd, int cmp, int count) +{ + int n = 0, diff; + + /* Replicate the test tag and compare. */ + cmp *= 0x11; + diff = *mem-- ^ cmp; + + if (!odd) { + goto start_even; + } + + while (1) { + /* Test odd tag. */ + if (unlikely((diff) & 0xf0)) { + break; + } + if (++n == count) { + break; + } + + start_even: + /* Test even tag. */ + if (unlikely((diff) & 0x0f)) { + break; + } + if (++n == count) { + break; + } + + diff = *mem-- ^ cmp; + } + return n; +} + /** * mte_probe_int() - helper for mte_probe and mte_check * @env: CPU environment @@ -1042,6 +1091,56 @@ uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, } } +uint64_t mte_mops_probe_rev(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag, tag_first, tag_last; + void *mem; + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + uint32_t n; + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* True probe; this will never fault */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, + w ? MMU_DATA_STORE : MMU_DATA_LOAD, + size, MMU_DATA_LOAD, true, 0); + if (!mem) { + return size; + } + + /* + * TODO: checkNrev() is not designed for checks of the size we expect + * for FEAT_MOPS operations, so we should implement this differently. + * Maybe we should do something like + * if (region start and size are aligned nicely) { + * do direct loads of 64 tag bits at a time; + * } else { + * call checkN() + * } + */ + /* Round the bounds to the tag granule, and compute the number of tags. */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_first = QEMU_ALIGN_DOWN(ptr - (size - 1), TAG_GRANULE); + tag_last = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); + tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; + n = checkNrev(mem, ptr & TAG_GRANULE, ptr_tag, tag_count); + if (likely(n == tag_count)) { + return size; + } + + /* + * Failure; for the first granule, it's at @ptr. Otherwise + * it's at the last byte of the nth granule. Calculate how + * many bytes we can access without hitting that failure. + */ + if (n == 0) { + return 0; + } else { + return (n - 1) * TAG_GRANULE + ((ptr + 1) - tag_last); + } +} + void mte_mops_set_tags(CPUARMState *env, uint64_t ptr, uint64_t size, uint32_t desc) { From 5d7b37b5f675d9cee0c9c1f8b386b3daa3cc2d9a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:33 +0100 Subject: [PATCH 24/80] target/arm: Implement the CPY* instructions The FEAT_MOPS CPY* instructions implement memory copies. These come in both "always forwards" (memcpy-style) and "overlap OK" (memmove-style) flavours. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-12-peter.maydell@linaro.org --- target/arm/tcg/a64.decode | 14 + target/arm/tcg/helper-a64.c | 454 +++++++++++++++++++++++++++++++++ target/arm/tcg/helper-a64.h | 7 + target/arm/tcg/translate-a64.c | 60 +++++ 4 files changed, 535 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index a202faa17b..0cf1147074 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -575,3 +575,17 @@ SETE 00 011001110 ..... 10 . . 01 ..... ..... @set SETGP 00 011101110 ..... 00 . . 01 ..... ..... @set SETGM 00 011101110 ..... 01 . . 01 ..... ..... @set SETGE 00 011101110 ..... 10 . . 01 ..... ..... @set + +# Memmove/Memcopy: the CPY insns allow overlapping src/dest and +# copy in the correct direction; the CPYF insns always copy forwards. +# +# options has the nontemporal and unpriv bits for src and dest +&cpy rs rn rd options +@cpy .. ... . ..... rs:5 options:4 .. rn:5 rd:5 &cpy + +CPYFP 00 011 0 01000 ..... .... 01 ..... ..... @cpy +CPYFM 00 011 0 01010 ..... .... 01 ..... ..... @cpy +CPYFE 00 011 0 01100 ..... .... 01 ..... ..... @cpy +CPYP 00 011 1 01000 ..... .... 01 ..... ..... @cpy +CPYM 00 011 1 01010 ..... .... 01 ..... ..... @cpy +CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 2cf89184d7..84f54750fc 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -1048,6 +1048,15 @@ static uint64_t page_limit(uint64_t addr) return TARGET_PAGE_ALIGN(addr + 1) - addr; } +/* + * Return the number of bytes we can copy starting from addr and working + * backwards without crossing a page boundary. + */ +static uint64_t page_limit_rev(uint64_t addr) +{ + return (addr & ~TARGET_PAGE_MASK) + 1; +} + /* * Perform part of a memory set on an area of guest memory starting at * toaddr (a dirty address) and extending for setsize bytes. @@ -1392,3 +1401,448 @@ void HELPER(setge)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) { do_sete(env, syndrome, mtedesc, set_step_tags, true, GETPC()); } + +/* + * Perform part of a memory copy from the guest memory at fromaddr + * and extending for copysize bytes, to the guest memory at + * toaddr. Both addreses are dirty. + * + * Returns the number of bytes actually set, which might be less than + * copysize; the caller should loop until the whole copy has been done. + * The caller should ensure that the guest registers are correct + * for the possibility that the first byte of the copy encounters + * an exception or watchpoint. We guarantee not to take any faults + * for bytes other than the first. + */ +static uint64_t copy_step(CPUARMState *env, uint64_t toaddr, uint64_t fromaddr, + uint64_t copysize, int wmemidx, int rmemidx, + uint32_t *wdesc, uint32_t *rdesc, uintptr_t ra) +{ + void *rmem; + void *wmem; + + /* Don't cross a page boundary on either source or destination */ + copysize = MIN(copysize, page_limit(toaddr)); + copysize = MIN(copysize, page_limit(fromaddr)); + /* + * Handle MTE tag checks: either handle the tag mismatch for byte 0, + * or else copy up to but not including the byte with the mismatch. + */ + if (*rdesc) { + uint64_t mtesize = mte_mops_probe(env, fromaddr, copysize, *rdesc); + if (mtesize == 0) { + mte_check_fail(env, *rdesc, fromaddr, ra); + *rdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + if (*wdesc) { + uint64_t mtesize = mte_mops_probe(env, toaddr, copysize, *wdesc); + if (mtesize == 0) { + mte_check_fail(env, *wdesc, toaddr, ra); + *wdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + + toaddr = useronly_clean_ptr(toaddr); + fromaddr = useronly_clean_ptr(fromaddr); + /* Trapless lookup of whether we can get a host memory pointer */ + wmem = tlb_vaddr_to_host(env, toaddr, MMU_DATA_STORE, wmemidx); + rmem = tlb_vaddr_to_host(env, fromaddr, MMU_DATA_LOAD, rmemidx); + +#ifndef CONFIG_USER_ONLY + /* + * If we don't have host memory for both source and dest then just + * do a single byte copy. This will handle watchpoints, invalid pages, + * etc correctly. For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + if (unlikely(!rmem || !wmem)) { + uint8_t byte; + if (rmem) { + byte = *(uint8_t *)rmem; + } else { + byte = cpu_ldub_mmuidx_ra(env, fromaddr, rmemidx, ra); + } + if (wmem) { + *(uint8_t *)wmem = byte; + } else { + cpu_stb_mmuidx_ra(env, toaddr, byte, wmemidx, ra); + } + return 1; + } +#endif + /* Easy case: just memmove the host memory */ + memmove(wmem, rmem, copysize); + return copysize; +} + +/* + * Do part of a backwards memory copy. Here toaddr and fromaddr point + * to the *last* byte to be copied. + */ +static uint64_t copy_step_rev(CPUARMState *env, uint64_t toaddr, + uint64_t fromaddr, + uint64_t copysize, int wmemidx, int rmemidx, + uint32_t *wdesc, uint32_t *rdesc, uintptr_t ra) +{ + void *rmem; + void *wmem; + + /* Don't cross a page boundary on either source or destination */ + copysize = MIN(copysize, page_limit_rev(toaddr)); + copysize = MIN(copysize, page_limit_rev(fromaddr)); + + /* + * Handle MTE tag checks: either handle the tag mismatch for byte 0, + * or else copy up to but not including the byte with the mismatch. + */ + if (*rdesc) { + uint64_t mtesize = mte_mops_probe_rev(env, fromaddr, copysize, *rdesc); + if (mtesize == 0) { + mte_check_fail(env, *rdesc, fromaddr, ra); + *rdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + if (*wdesc) { + uint64_t mtesize = mte_mops_probe_rev(env, toaddr, copysize, *wdesc); + if (mtesize == 0) { + mte_check_fail(env, *wdesc, toaddr, ra); + *wdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + + toaddr = useronly_clean_ptr(toaddr); + fromaddr = useronly_clean_ptr(fromaddr); + /* Trapless lookup of whether we can get a host memory pointer */ + wmem = tlb_vaddr_to_host(env, toaddr, MMU_DATA_STORE, wmemidx); + rmem = tlb_vaddr_to_host(env, fromaddr, MMU_DATA_LOAD, rmemidx); + +#ifndef CONFIG_USER_ONLY + /* + * If we don't have host memory for both source and dest then just + * do a single byte copy. This will handle watchpoints, invalid pages, + * etc correctly. For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + if (unlikely(!rmem || !wmem)) { + uint8_t byte; + if (rmem) { + byte = *(uint8_t *)rmem; + } else { + byte = cpu_ldub_mmuidx_ra(env, fromaddr, rmemidx, ra); + } + if (wmem) { + *(uint8_t *)wmem = byte; + } else { + cpu_stb_mmuidx_ra(env, toaddr, byte, wmemidx, ra); + } + return 1; + } +#endif + /* + * Easy case: just memmove the host memory. Note that wmem and + * rmem here point to the *last* byte to copy. + */ + memmove(wmem - (copysize - 1), rmem - (copysize - 1), copysize); + return copysize; +} + +/* + * for the Memory Copy operation, our implementation chooses always + * to use "option A", where we update Xd and Xs to the final addresses + * in the CPYP insn, and then in CPYM and CPYE only need to update Xn. + * + * @env: CPU + * @syndrome: syndrome value for mismatch exceptions + * (also contains the register numbers we need to use) + * @wdesc: MTE descriptor for the writes (destination) + * @rdesc: MTE descriptor for the reads (source) + * @move: true if this is CPY (memmove), false for CPYF (memcpy forwards) + */ +static void do_cpyp(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc, uint32_t move, uintptr_t ra) +{ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint32_t rmemidx = FIELD_EX32(rdesc, MTEDESC, MIDX); + uint32_t wmemidx = FIELD_EX32(wdesc, MTEDESC, MIDX); + bool forwards = true; + uint64_t toaddr = env->xregs[rd]; + uint64_t fromaddr = env->xregs[rs]; + uint64_t copysize = env->xregs[rn]; + uint64_t stagecopysize, step; + + check_mops_enabled(env, ra); + + + if (move) { + /* + * Copy backwards if necessary. The direction for a non-overlapping + * copy is IMPDEF; we choose forwards. + */ + if (copysize > 0x007FFFFFFFFFFFFFULL) { + copysize = 0x007FFFFFFFFFFFFFULL; + } + uint64_t fs = extract64(fromaddr, 0, 56); + uint64_t ts = extract64(toaddr, 0, 56); + uint64_t fe = extract64(fromaddr + copysize, 0, 56); + + if (fs < ts && fe > ts) { + forwards = false; + } + } else { + if (copysize > INT64_MAX) { + copysize = INT64_MAX; + } + } + + if (!mte_checks_needed(fromaddr, rdesc)) { + rdesc = 0; + } + if (!mte_checks_needed(toaddr, wdesc)) { + wdesc = 0; + } + + if (forwards) { + stagecopysize = MIN(copysize, page_limit(toaddr)); + stagecopysize = MIN(stagecopysize, page_limit(fromaddr)); + while (stagecopysize) { + env->xregs[rd] = toaddr; + env->xregs[rs] = fromaddr; + env->xregs[rn] = copysize; + step = copy_step(env, toaddr, fromaddr, stagecopysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr += step; + fromaddr += step; + copysize -= step; + stagecopysize -= step; + } + /* Insn completed, so update registers to the Option A format */ + env->xregs[rd] = toaddr + copysize; + env->xregs[rs] = fromaddr + copysize; + env->xregs[rn] = -copysize; + } else { + /* + * In a reverse copy the to and from addrs in Xs and Xd are the start + * of the range, but it's more convenient for us to work with pointers + * to the last byte being copied. + */ + toaddr += copysize - 1; + fromaddr += copysize - 1; + stagecopysize = MIN(copysize, page_limit_rev(toaddr)); + stagecopysize = MIN(stagecopysize, page_limit_rev(fromaddr)); + while (stagecopysize) { + env->xregs[rn] = copysize; + step = copy_step_rev(env, toaddr, fromaddr, stagecopysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + copysize -= step; + stagecopysize -= step; + toaddr -= step; + fromaddr -= step; + } + /* + * Insn completed, so update registers to the Option A format. + * For a reverse copy this is no different to the CPYP input format. + */ + env->xregs[rn] = copysize; + } + + /* Set NZCV = 0000 to indicate we are an Option A implementation */ + env->NF = 0; + env->ZF = 1; /* our env->ZF encoding is inverted */ + env->CF = 0; + env->VF = 0; + return; +} + +void HELPER(cpyp)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpyp(env, syndrome, wdesc, rdesc, true, GETPC()); +} + +void HELPER(cpyfp)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpyp(env, syndrome, wdesc, rdesc, false, GETPC()); +} + +static void do_cpym(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc, uint32_t move, uintptr_t ra) +{ + /* Main: we choose to copy until less than a page remaining */ + CPUState *cs = env_cpu(env); + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint32_t rmemidx = FIELD_EX32(rdesc, MTEDESC, MIDX); + uint32_t wmemidx = FIELD_EX32(wdesc, MTEDESC, MIDX); + bool forwards = true; + uint64_t toaddr, fromaddr, copysize, step; + + check_mops_enabled(env, ra); + + /* We choose to NOP out "no data to copy" before consistency checks */ + if (env->xregs[rn] == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + if (move) { + forwards = (int64_t)env->xregs[rn] < 0; + } + + if (forwards) { + toaddr = env->xregs[rd] + env->xregs[rn]; + fromaddr = env->xregs[rs] + env->xregs[rn]; + copysize = -env->xregs[rn]; + } else { + copysize = env->xregs[rn]; + /* This toaddr and fromaddr point to the *last* byte to copy */ + toaddr = env->xregs[rd] + copysize - 1; + fromaddr = env->xregs[rs] + copysize - 1; + } + + if (!mte_checks_needed(fromaddr, rdesc)) { + rdesc = 0; + } + if (!mte_checks_needed(toaddr, wdesc)) { + wdesc = 0; + } + + /* Our implementation has no particular parameter requirements for CPYM */ + + /* Do the actual memmove */ + if (forwards) { + while (copysize >= TARGET_PAGE_SIZE) { + step = copy_step(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr += step; + fromaddr += step; + copysize -= step; + env->xregs[rn] = -copysize; + if (copysize >= TARGET_PAGE_SIZE && + unlikely(cpu_loop_exit_requested(cs))) { + cpu_loop_exit_restore(cs, ra); + } + } + } else { + while (copysize >= TARGET_PAGE_SIZE) { + step = copy_step_rev(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr -= step; + fromaddr -= step; + copysize -= step; + env->xregs[rn] = copysize; + if (copysize >= TARGET_PAGE_SIZE && + unlikely(cpu_loop_exit_requested(cs))) { + cpu_loop_exit_restore(cs, ra); + } + } + } +} + +void HELPER(cpym)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpym(env, syndrome, wdesc, rdesc, true, GETPC()); +} + +void HELPER(cpyfm)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpym(env, syndrome, wdesc, rdesc, false, GETPC()); +} + +static void do_cpye(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc, uint32_t move, uintptr_t ra) +{ + /* Epilogue: do the last partial page */ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint32_t rmemidx = FIELD_EX32(rdesc, MTEDESC, MIDX); + uint32_t wmemidx = FIELD_EX32(wdesc, MTEDESC, MIDX); + bool forwards = true; + uint64_t toaddr, fromaddr, copysize, step; + + check_mops_enabled(env, ra); + + /* We choose to NOP out "no data to copy" before consistency checks */ + if (env->xregs[rn] == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + if (move) { + forwards = (int64_t)env->xregs[rn] < 0; + } + + if (forwards) { + toaddr = env->xregs[rd] + env->xregs[rn]; + fromaddr = env->xregs[rs] + env->xregs[rn]; + copysize = -env->xregs[rn]; + } else { + copysize = env->xregs[rn]; + /* This toaddr and fromaddr point to the *last* byte to copy */ + toaddr = env->xregs[rd] + copysize - 1; + fromaddr = env->xregs[rs] + copysize - 1; + } + + if (!mte_checks_needed(fromaddr, rdesc)) { + rdesc = 0; + } + if (!mte_checks_needed(toaddr, wdesc)) { + wdesc = 0; + } + + /* Check the size; we don't want to have do a check-for-interrupts */ + if (copysize >= TARGET_PAGE_SIZE) { + raise_exception_ra(env, EXCP_UDEF, syndrome, + mops_mismatch_exception_target_el(env), ra); + } + + /* Do the actual memmove */ + if (forwards) { + while (copysize > 0) { + step = copy_step(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr += step; + fromaddr += step; + copysize -= step; + env->xregs[rn] = -copysize; + } + } else { + while (copysize > 0) { + step = copy_step_rev(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr -= step; + fromaddr -= step; + copysize -= step; + env->xregs[rn] = copysize; + } + } +} + +void HELPER(cpye)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpye(env, syndrome, wdesc, rdesc, true, GETPC()); +} + +void HELPER(cpyfe)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpye(env, syndrome, wdesc, rdesc, false, GETPC()); +} diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 10a9910712..575a5dab7d 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -124,3 +124,10 @@ DEF_HELPER_3(sete, void, env, i32, i32) DEF_HELPER_3(setgp, void, env, i32, i32) DEF_HELPER_3(setgm, void, env, i32, i32) DEF_HELPER_3(setge, void, env, i32, i32) + +DEF_HELPER_4(cpyp, void, env, i32, i32, i32) +DEF_HELPER_4(cpym, void, env, i32, i32, i32) +DEF_HELPER_4(cpye, void, env, i32, i32, i32) +DEF_HELPER_4(cpyfp, void, env, i32, i32, i32) +DEF_HELPER_4(cpyfm, void, env, i32, i32, i32) +DEF_HELPER_4(cpyfe, void, env, i32, i32, i32) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 27bb3039b4..97f25b4451 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4019,6 +4019,66 @@ TRANS_FEAT(SETGP, aa64_mops, do_SET, a, false, true, gen_helper_setgp) TRANS_FEAT(SETGM, aa64_mops, do_SET, a, false, true, gen_helper_setgm) TRANS_FEAT(SETGE, aa64_mops, do_SET, a, true, true, gen_helper_setge) +typedef void CpyFn(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32); + +static bool do_CPY(DisasContext *s, arg_cpy *a, bool is_epilogue, CpyFn fn) +{ + int rmemidx, wmemidx; + uint32_t syndrome, rdesc = 0, wdesc = 0; + bool wunpriv = extract32(a->options, 0, 1); + bool runpriv = extract32(a->options, 1, 1); + + /* + * UNPREDICTABLE cases: we choose to UNDEF, which allows + * us to pull this check before the CheckMOPSEnabled() test + * (which we do in the helper function) + */ + if (a->rs == a->rn || a->rs == a->rd || a->rn == a->rd || + a->rd == 31 || a->rs == 31 || a->rn == 31) { + return false; + } + + rmemidx = get_a64_user_mem_index(s, runpriv); + wmemidx = get_a64_user_mem_index(s, wunpriv); + + /* + * We pass option_a == true, matching our implementation; + * we pass wrong_option == false: helper function may set that bit. + */ + syndrome = syn_mop(false, false, a->options, is_epilogue, + false, true, a->rd, a->rs, a->rn); + + /* If we need to do MTE tag checking, assemble the descriptors */ + if (s->mte_active[runpriv]) { + rdesc = FIELD_DP32(rdesc, MTEDESC, TBI, s->tbid); + rdesc = FIELD_DP32(rdesc, MTEDESC, TCMA, s->tcma); + } + if (s->mte_active[wunpriv]) { + wdesc = FIELD_DP32(wdesc, MTEDESC, TBI, s->tbid); + wdesc = FIELD_DP32(wdesc, MTEDESC, TCMA, s->tcma); + wdesc = FIELD_DP32(wdesc, MTEDESC, WRITE, true); + } + /* The helper function needs these parts of the descriptor regardless */ + rdesc = FIELD_DP32(rdesc, MTEDESC, MIDX, rmemidx); + wdesc = FIELD_DP32(wdesc, MTEDESC, MIDX, wmemidx); + + /* + * The helper needs the register numbers, but since they're in + * the syndrome anyway, we let it extract them from there rather + * than passing in an extra three integer arguments. + */ + fn(cpu_env, tcg_constant_i32(syndrome), tcg_constant_i32(wdesc), + tcg_constant_i32(rdesc)); + return true; +} + +TRANS_FEAT(CPYP, aa64_mops, do_CPY, a, false, gen_helper_cpyp) +TRANS_FEAT(CPYM, aa64_mops, do_CPY, a, false, gen_helper_cpym) +TRANS_FEAT(CPYE, aa64_mops, do_CPY, a, true, gen_helper_cpye) +TRANS_FEAT(CPYFP, aa64_mops, do_CPY, a, false, gen_helper_cpyfp) +TRANS_FEAT(CPYFM, aa64_mops, do_CPY, a, false, gen_helper_cpyfm) +TRANS_FEAT(CPYFE, aa64_mops, do_CPY, a, true, gen_helper_cpyfe) + typedef void ArithTwoOp(TCGv_i64, TCGv_i64, TCGv_i64); static bool gen_rri(DisasContext *s, arg_rri_sf *a, From 706a92fbfa3932020d232890d1caf648b8e9eff6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Sep 2023 15:04:34 +0100 Subject: [PATCH 25/80] target/arm: Enable FEAT_MOPS for CPU 'max' Enable FEAT_MOPS on the AArch64 'max' CPU, and add it to the list of features we implement. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20230912140434.1333369-13-peter.maydell@linaro.org --- docs/system/arm/emulation.rst | 1 + linux-user/elfload.c | 1 + target/arm/tcg/cpu64.c | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 1fb6a2e8c3..965cbf84c5 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -58,6 +58,7 @@ the following architecture extensions: - FEAT_LSE (Large System Extensions) - FEAT_LSE2 (Large System Extensions v2) - FEAT_LVA (Large Virtual Address space) +- FEAT_MOPS (Standardization of memory operations) - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 203a2b790d..db75cd4b33 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -816,6 +816,7 @@ uint32_t get_elf_hwcap2(void) GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); + GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); return hwcaps; } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 57abaea00c..68928e5127 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1028,6 +1028,7 @@ void aarch64_max_tcg_initfn(Object *obj) cpu->isar.id_aa64isar1 = t; t = cpu->isar.id_aa64isar2; + t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ cpu->isar.id_aa64isar2 = t; From d71c3d305980b38c6e6794da7401172ac0fec891 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Aug 2023 16:58:45 +0100 Subject: [PATCH 26/80] audio/jackaudio: Avoid dynamic stack allocation in qjack_client_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid a dynamic stack allocation in qjack_client_init(), by using a g_autofree heap allocation instead. (We stick with allocate + snprintf() because the JACK API requires the name to be no more than its maximum size, so g_strdup_printf() would require an extra truncation step.) The codebase has very few VLAs, and if we can get rid of them all we can make the compiler error on new additions. This is a defensive measure against security bugs where an on-stack dynamic allocation isn't correctly size-checked (e.g. CVE-2021-3527). Signed-off-by: Peter Maydell Reviewed-by: Marc-André Lureau Reviewed-by: Francisco Iglesias Reviewed-by: Christian Schoenebeck Message-id: 20230818155846.1651287-2-peter.maydell@linaro.org --- audio/jackaudio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/audio/jackaudio.c b/audio/jackaudio.c index 5bdf3d7a78..7cb2a49f97 100644 --- a/audio/jackaudio.c +++ b/audio/jackaudio.c @@ -400,7 +400,8 @@ static void qjack_client_connect_ports(QJackClient *c) static int qjack_client_init(QJackClient *c) { jack_status_t status; - char client_name[jack_client_name_size()]; + int client_name_len = jack_client_name_size(); /* includes NUL */ + g_autofree char *client_name = g_new(char, client_name_len); jack_options_t options = JackNullOption; if (c->state == QJACK_STATE_RUNNING) { @@ -409,7 +410,7 @@ static int qjack_client_init(QJackClient *c) c->connect_ports = true; - snprintf(client_name, sizeof(client_name), "%s-%s", + snprintf(client_name, client_name_len, "%s-%s", c->out ? "out" : "in", c->opt->client_name ? c->opt->client_name : audio_application_name()); From 07ffc4b90f0c2b4ec13ba804fdffc56d49dff93c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Aug 2023 16:58:46 +0100 Subject: [PATCH 27/80] audio/jackaudio: Avoid dynamic stack allocation in qjack_process() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid a dynamic stack allocation in qjack_process(). Since this function is a JACK process callback, we are not permitted to malloc() here, so we allocate a working buffer in qjack_client_init() instead. The codebase has very few VLAs, and if we can get rid of them all we can make the compiler error on new additions. This is a defensive measure against security bugs where an on-stack dynamic allocation isn't correctly size-checked (e.g. CVE-2021-3527). Signed-off-by: Peter Maydell Reviewed-by: Marc-André Lureau Reviewed-by: Francisco Iglesias Reviewed-by: Christian Schoenebeck Message-id: 20230818155846.1651287-3-peter.maydell@linaro.org --- audio/jackaudio.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/audio/jackaudio.c b/audio/jackaudio.c index 7cb2a49f97..e1eaa3477d 100644 --- a/audio/jackaudio.c +++ b/audio/jackaudio.c @@ -70,6 +70,9 @@ typedef struct QJackClient { int buffersize; jack_port_t **port; QJackBuffer fifo; + + /* Used as workspace by qjack_process() */ + float **process_buffers; } QJackClient; @@ -267,22 +270,21 @@ static int qjack_process(jack_nframes_t nframes, void *arg) } /* get the buffers for the ports */ - float *buffers[c->nchannels]; for (int i = 0; i < c->nchannels; ++i) { - buffers[i] = jack_port_get_buffer(c->port[i], nframes); + c->process_buffers[i] = jack_port_get_buffer(c->port[i], nframes); } if (c->out) { if (likely(c->enabled)) { - qjack_buffer_read_l(&c->fifo, buffers, nframes); + qjack_buffer_read_l(&c->fifo, c->process_buffers, nframes); } else { for (int i = 0; i < c->nchannels; ++i) { - memset(buffers[i], 0, nframes * sizeof(float)); + memset(c->process_buffers[i], 0, nframes * sizeof(float)); } } } else { if (likely(c->enabled)) { - qjack_buffer_write_l(&c->fifo, buffers, nframes); + qjack_buffer_write_l(&c->fifo, c->process_buffers, nframes); } } @@ -448,6 +450,9 @@ static int qjack_client_init(QJackClient *c) jack_get_client_name(c->client)); } + /* Allocate working buffer for process callback */ + c->process_buffers = g_new(float *, c->nchannels); + jack_set_process_callback(c->client, qjack_process , c); jack_set_port_registration_callback(c->client, qjack_port_registration, c); jack_set_xrun_callback(c->client, qjack_xrun, c); @@ -579,6 +584,7 @@ static void qjack_client_fini_locked(QJackClient *c) qjack_buffer_free(&c->fifo); g_free(c->port); + g_free(c->process_buffers); c->state = QJACK_STATE_DISCONNECTED; /* fallthrough */ From 058262e0a8b23b7b45003749efe5246294195324 Mon Sep 17 00:00:00 2001 From: Marcin Juszkiewicz Date: Wed, 13 Sep 2023 16:06:10 +0200 Subject: [PATCH 28/80] sbsa-ref: add non-secure EL2 virtual timer Armv8.1+ cpus have Virtual Host Extension (VHE) which added non-secure EL2 virtual timer. This change adds it to fullfil Arm BSA (Base System Architecture) requirements. Signed-off-by: Marcin Juszkiewicz Message-id: 20230913140610.214893-2-marcin.juszkiewicz@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/sbsa-ref.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index bc89eb4806..3c7dfcd6dc 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -61,6 +61,7 @@ #define ARCH_TIMER_S_EL1_IRQ 13 #define ARCH_TIMER_NS_EL1_IRQ 14 #define ARCH_TIMER_NS_EL2_IRQ 10 +#define ARCH_TIMER_NS_EL2_VIRT_IRQ 12 enum { SBSA_FLASH, @@ -489,6 +490,7 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + [GTIMER_HYPVIRT] = ARCH_TIMER_NS_EL2_VIRT_IRQ, }; for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { From 3c407ec67f94f8dc189f3e47a6419431d15ff084 Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Fri, 15 Sep 2023 20:01:49 +0300 Subject: [PATCH 29/80] elf2dmp: replace PE export name check with PDB name check PE export name check introduced in d399d6b179 isn't reliable enough, because a page with the export directory may be not present for some reason. On the other hand, elf2dmp retrieves the PDB name in any case. It can be also used to check that a PE image is the kernel image. So, check PDB name when searching for Windows kernel image. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2165917 Signed-off-by: Viktor Prutyanov Reviewed-by: Akihiko Odaki Message-id: 20230915170153.10959-2-viktor@daynix.com Signed-off-by: Peter Maydell --- contrib/elf2dmp/main.c | 93 +++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 60 deletions(-) diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index 6d4d18501a..bb6744c0cd 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -411,89 +411,64 @@ static int write_dump(struct pa_space *ps, return fclose(dmp_file); } -static bool pe_check_export_name(uint64_t base, void *start_addr, - struct va_space *vs) -{ - IMAGE_EXPORT_DIRECTORY export_dir; - const char *pe_name; - - if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_EXPORT_DIRECTORY, - &export_dir, sizeof(export_dir), vs)) { - return false; - } - - pe_name = va_space_resolve(vs, base + export_dir.Name); - if (!pe_name) { - return false; - } - - return !strcmp(pe_name, PE_NAME); -} - -static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr, - char *hash, struct va_space *vs) +static bool pe_check_pdb_name(uint64_t base, void *start_addr, + struct va_space *vs, OMFSignatureRSDS *rsds) { const char sign_rsds[4] = "RSDS"; IMAGE_DEBUG_DIRECTORY debug_dir; - OMFSignatureRSDS rsds; - char *pdb_name; - size_t pdb_name_sz; - size_t i; + char pdb_name[sizeof(PDB_NAME)]; if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_DEBUG_DIRECTORY, &debug_dir, sizeof(debug_dir), vs)) { eprintf("Failed to get Debug Directory\n"); - return 1; + return false; } if (debug_dir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) { - return 1; + eprintf("Debug Directory type is not CodeView\n"); + return false; } if (va_space_rw(vs, base + debug_dir.AddressOfRawData, - &rsds, sizeof(rsds), 0)) { - return 1; + rsds, sizeof(*rsds), 0)) { + eprintf("Failed to resolve OMFSignatureRSDS\n"); + return false; } - printf("CodeView signature is \'%.4s\'\n", rsds.Signature); - - if (memcmp(&rsds.Signature, sign_rsds, sizeof(sign_rsds))) { - return 1; + if (memcmp(&rsds->Signature, sign_rsds, sizeof(sign_rsds))) { + eprintf("CodeView signature is \'%.4s\', \'%s\' expected\n", + rsds->Signature, sign_rsds); + return false; } - pdb_name_sz = debug_dir.SizeOfData - sizeof(rsds); - pdb_name = malloc(pdb_name_sz); - if (!pdb_name) { - return 1; + if (debug_dir.SizeOfData - sizeof(*rsds) != sizeof(PDB_NAME)) { + eprintf("PDB name size doesn't match\n"); + return false; } if (va_space_rw(vs, base + debug_dir.AddressOfRawData + - offsetof(OMFSignatureRSDS, name), pdb_name, pdb_name_sz, 0)) { - free(pdb_name); - return 1; + offsetof(OMFSignatureRSDS, name), pdb_name, sizeof(PDB_NAME), + 0)) { + eprintf("Failed to resolve PDB name\n"); + return false; } printf("PDB name is \'%s\', \'%s\' expected\n", pdb_name, PDB_NAME); - if (strcmp(pdb_name, PDB_NAME)) { - eprintf("Unexpected PDB name, it seems the kernel isn't found\n"); - free(pdb_name); - return 1; - } + return !strcmp(pdb_name, PDB_NAME); +} - free(pdb_name); - - sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds.guid.a, rsds.guid.b, - rsds.guid.c, rsds.guid.d[0], rsds.guid.d[1]); +static void pe_get_pdb_symstore_hash(OMFSignatureRSDS *rsds, char *hash) +{ + sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds->guid.a, rsds->guid.b, + rsds->guid.c, rsds->guid.d[0], rsds->guid.d[1]); hash += 20; - for (i = 0; i < 6; i++, hash += 2) { - sprintf(hash, "%.02x", rsds.guid.e[i]); + for (unsigned int i = 0; i < 6; i++, hash += 2) { + sprintf(hash, "%.02x", rsds->guid.e[i]); } - sprintf(hash, "%.01x", rsds.age); - - return 0; + sprintf(hash, "%.01x", rsds->age); } int main(int argc, char *argv[]) @@ -515,6 +490,7 @@ int main(int argc, char *argv[]) KDDEBUGGER_DATA64 *kdbg; uint64_t KdVersionBlock; bool kernel_found = false; + OMFSignatureRSDS rsds; if (argc != 3) { eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]); @@ -562,7 +538,8 @@ int main(int argc, char *argv[]) } if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */ - if (pe_check_export_name(KernBase, nt_start_addr, &vs)) { + printf("Checking candidate KernBase = 0x%016"PRIx64"\n", KernBase); + if (pe_check_pdb_name(KernBase, nt_start_addr, &vs, &rsds)) { kernel_found = true; break; } @@ -578,11 +555,7 @@ int main(int argc, char *argv[]) printf("KernBase = 0x%016"PRIx64", signature is \'%.2s\'\n", KernBase, (char *)nt_start_addr); - if (pe_get_pdb_symstore_hash(KernBase, nt_start_addr, pdb_hash, &vs)) { - eprintf("Failed to get PDB symbol store hash\n"); - err = 1; - goto out_ps; - } + pe_get_pdb_symstore_hash(&rsds, pdb_hash); sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME); printf("PDB URL is %s\n", pdb_url); From d5c27a53d51175a2b89208b050488aaba74de131 Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Fri, 15 Sep 2023 20:01:50 +0300 Subject: [PATCH 30/80] elf2dmp: introduce physical block alignment Physical memory ranges may not be aligned to page size in QEMU ELF, but DMP can only contain page-aligned runs. So, align them. Signed-off-by: Viktor Prutyanov Reviewed-by: Akihiko Odaki Message-id: 20230915170153.10959-3-viktor@daynix.com Signed-off-by: Peter Maydell --- contrib/elf2dmp/addrspace.c | 31 +++++++++++++++++++++++++++++-- contrib/elf2dmp/addrspace.h | 1 + contrib/elf2dmp/main.c | 5 +++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/contrib/elf2dmp/addrspace.c b/contrib/elf2dmp/addrspace.c index 0b04cba00e..64b5d680ad 100644 --- a/contrib/elf2dmp/addrspace.c +++ b/contrib/elf2dmp/addrspace.c @@ -14,7 +14,7 @@ static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa) for (i = 0; i < ps->block_nr; i++) { if (ps->block[i].paddr <= pa && - pa <= ps->block[i].paddr + ps->block[i].size) { + pa < ps->block[i].paddr + ps->block[i].size) { return ps->block + i; } } @@ -33,6 +33,30 @@ static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa) return block->addr + (pa - block->paddr); } +static void pa_block_align(struct pa_block *b) +{ + uint64_t low_align = ((b->paddr - 1) | ELF2DMP_PAGE_MASK) + 1 - b->paddr; + uint64_t high_align = (b->paddr + b->size) & ELF2DMP_PAGE_MASK; + + if (low_align == 0 && high_align == 0) { + return; + } + + if (low_align + high_align < b->size) { + printf("Block 0x%"PRIx64"+:0x%"PRIx64" will be aligned to " + "0x%"PRIx64"+:0x%"PRIx64"\n", b->paddr, b->size, + b->paddr + low_align, b->size - low_align - high_align); + b->size -= low_align + high_align; + } else { + printf("Block 0x%"PRIx64"+:0x%"PRIx64" is too small to align\n", + b->paddr, b->size); + b->size = 0; + } + + b->addr += low_align; + b->paddr += low_align; +} + int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf) { Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map); @@ -60,10 +84,13 @@ int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf) .paddr = phdr[i].p_paddr, .size = phdr[i].p_filesz, }; - block_i++; + pa_block_align(&ps->block[block_i]); + block_i = ps->block[block_i].size ? (block_i + 1) : block_i; } } + ps->block_nr = block_i; + return 0; } diff --git a/contrib/elf2dmp/addrspace.h b/contrib/elf2dmp/addrspace.h index 00b44c1218..039c70c5b0 100644 --- a/contrib/elf2dmp/addrspace.h +++ b/contrib/elf2dmp/addrspace.h @@ -12,6 +12,7 @@ #define ELF2DMP_PAGE_BITS 12 #define ELF2DMP_PAGE_SIZE (1ULL << ELF2DMP_PAGE_BITS) +#define ELF2DMP_PAGE_MASK (ELF2DMP_PAGE_SIZE - 1) #define ELF2DMP_PFN_MASK (~(ELF2DMP_PAGE_SIZE - 1)) #define INVALID_PA UINT64_MAX diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index bb6744c0cd..b7e3930164 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -400,9 +400,10 @@ static int write_dump(struct pa_space *ps, for (i = 0; i < ps->block_nr; i++) { struct pa_block *b = &ps->block[i]; - printf("Writing block #%zu/%zu to file...\n", i, ps->block_nr); + printf("Writing block #%zu/%zu of %"PRIu64" bytes to file...\n", i, + ps->block_nr, b->size); if (fwrite(b->addr, b->size, 1, dmp_file) != 1) { - eprintf("Failed to write dump header\n"); + eprintf("Failed to write block\n"); fclose(dmp_file); return 1; } From 9b7dcd8ff4e5a0b6a976027f6d78c0b55c30607d Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Fri, 15 Sep 2023 20:01:51 +0300 Subject: [PATCH 31/80] elf2dmp: introduce merging of physical memory runs DMP supports 42 physical memory runs at most. So, merge adjacent physical memory ranges from QEMU ELF when possible to minimize total number of runs. Signed-off-by: Viktor Prutyanov Reviewed-by: Akihiko Odaki Message-id: 20230915170153.10959-4-viktor@daynix.com [PMM: fixed format string for printing size_t values] Signed-off-by: Peter Maydell --- contrib/elf2dmp/main.c | 56 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index b7e3930164..5db163bdbe 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -20,6 +20,7 @@ #define PE_NAME "ntoskrnl.exe" #define INITIAL_MXCSR 0x1f80 +#define MAX_NUMBER_OF_RUNS 42 typedef struct idt_desc { uint16_t offset1; /* offset bits 0..15 */ @@ -234,6 +235,42 @@ static int fix_dtb(struct va_space *vs, QEMU_Elf *qe) return 1; } +static void try_merge_runs(struct pa_space *ps, + WinDumpPhyMemDesc64 *PhysicalMemoryBlock) +{ + unsigned int merge_cnt = 0, run_idx = 0; + + PhysicalMemoryBlock->NumberOfRuns = 0; + + for (size_t idx = 0; idx < ps->block_nr; idx++) { + struct pa_block *blk = ps->block + idx; + struct pa_block *next = blk + 1; + + PhysicalMemoryBlock->NumberOfPages += blk->size / ELF2DMP_PAGE_SIZE; + + if (idx + 1 != ps->block_nr && blk->paddr + blk->size == next->paddr) { + printf("Block #%zu 0x%"PRIx64"+:0x%"PRIx64" and %u previous will be" + " merged\n", idx, blk->paddr, blk->size, merge_cnt); + merge_cnt++; + } else { + struct pa_block *first_merged = blk - merge_cnt; + + printf("Block #%zu 0x%"PRIx64"+:0x%"PRIx64" and %u previous will be" + " merged to 0x%"PRIx64"+:0x%"PRIx64" (run #%u)\n", + idx, blk->paddr, blk->size, merge_cnt, first_merged->paddr, + blk->paddr + blk->size - first_merged->paddr, run_idx); + PhysicalMemoryBlock->Run[run_idx] = (WinDumpPhyMemRun64) { + .BasePage = first_merged->paddr / ELF2DMP_PAGE_SIZE, + .PageCount = (blk->paddr + blk->size - first_merged->paddr) / + ELF2DMP_PAGE_SIZE, + }; + PhysicalMemoryBlock->NumberOfRuns++; + run_idx++; + merge_cnt = 0; + } + } +} + static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, struct va_space *vs, uint64_t KdDebuggerDataBlock, KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus) @@ -244,7 +281,6 @@ static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, KUSD_OFFSET_PRODUCT_TYPE); DBGKD_GET_VERSION64 kvb; WinDumpHeader64 h; - size_t i; QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= ELF2DMP_PAGE_SIZE); QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= ELF2DMP_PAGE_SIZE); @@ -282,13 +318,17 @@ static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, .RequiredDumpSpace = sizeof(h), }; - for (i = 0; i < ps->block_nr; i++) { - h.PhysicalMemoryBlock.NumberOfPages += - ps->block[i].size / ELF2DMP_PAGE_SIZE; - h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) { - .BasePage = ps->block[i].paddr / ELF2DMP_PAGE_SIZE, - .PageCount = ps->block[i].size / ELF2DMP_PAGE_SIZE, - }; + if (h.PhysicalMemoryBlock.NumberOfRuns <= MAX_NUMBER_OF_RUNS) { + for (size_t idx = 0; idx < ps->block_nr; idx++) { + h.PhysicalMemoryBlock.NumberOfPages += + ps->block[idx].size / ELF2DMP_PAGE_SIZE; + h.PhysicalMemoryBlock.Run[idx] = (WinDumpPhyMemRun64) { + .BasePage = ps->block[idx].paddr / ELF2DMP_PAGE_SIZE, + .PageCount = ps->block[idx].size / ELF2DMP_PAGE_SIZE, + }; + } + } else { + try_merge_runs(ps, &h.PhysicalMemoryBlock); } h.RequiredDumpSpace += From df7a75564e72b61213d5355c9ba8215244bb83bd Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Fri, 15 Sep 2023 20:01:52 +0300 Subject: [PATCH 32/80] elf2dmp: use Linux mmap with MAP_NORESERVE when possible Glib's g_mapped_file_new maps file with PROT_READ|PROT_WRITE and MAP_PRIVATE. This leads to premature physical memory allocation of dump file size on Linux hosts and may fail. On Linux, mapping the file with MAP_NORESERVE limits the allocation by available memory. Signed-off-by: Viktor Prutyanov Reviewed-by: Akihiko Odaki Message-id: 20230915170153.10959-5-viktor@daynix.com Signed-off-by: Peter Maydell --- contrib/elf2dmp/qemu_elf.c | 68 +++++++++++++++++++++++++++++++------- contrib/elf2dmp/qemu_elf.h | 2 ++ 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c index ebda60dcb8..de6ad744c6 100644 --- a/contrib/elf2dmp/qemu_elf.c +++ b/contrib/elf2dmp/qemu_elf.c @@ -165,10 +165,40 @@ static bool check_ehdr(QEMU_Elf *qe) return true; } -int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) +static int QEMU_Elf_map(QEMU_Elf *qe, const char *filename) { +#ifdef CONFIG_LINUX + struct stat st; + int fd; + + printf("Using Linux mmap\n"); + + fd = open(filename, O_RDONLY, 0); + if (fd == -1) { + eprintf("Failed to open ELF dump file \'%s\'\n", filename); + return 1; + } + + if (fstat(fd, &st)) { + eprintf("Failed to get size of ELF dump file\n"); + close(fd); + return 1; + } + qe->size = st.st_size; + + qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_NORESERVE, fd, 0); + if (qe->map == MAP_FAILED) { + eprintf("Failed to map ELF file\n"); + close(fd); + return 1; + } + + close(fd); +#else GError *gerr = NULL; - int err = 0; + + printf("Using GLib mmap\n"); qe->gmf = g_mapped_file_new(filename, TRUE, &gerr); if (gerr) { @@ -179,29 +209,43 @@ int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) qe->map = g_mapped_file_get_contents(qe->gmf); qe->size = g_mapped_file_get_length(qe->gmf); +#endif + + return 0; +} + +static void QEMU_Elf_unmap(QEMU_Elf *qe) +{ +#ifdef CONFIG_LINUX + munmap(qe->map, qe->size); +#else + g_mapped_file_unref(qe->gmf); +#endif +} + +int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) +{ + if (QEMU_Elf_map(qe, filename)) { + return 1; + } if (!check_ehdr(qe)) { eprintf("Input file has the wrong format\n"); - err = 1; - goto out_unmap; + QEMU_Elf_unmap(qe); + return 1; } if (init_states(qe)) { eprintf("Failed to extract QEMU CPU states\n"); - err = 1; - goto out_unmap; + QEMU_Elf_unmap(qe); + return 1; } return 0; - -out_unmap: - g_mapped_file_unref(qe->gmf); - - return err; } void QEMU_Elf_exit(QEMU_Elf *qe) { exit_states(qe); - g_mapped_file_unref(qe->gmf); + QEMU_Elf_unmap(qe); } diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h index b2f0d9cbc9..afa75f10b2 100644 --- a/contrib/elf2dmp/qemu_elf.h +++ b/contrib/elf2dmp/qemu_elf.h @@ -32,7 +32,9 @@ typedef struct QEMUCPUState { int is_system(QEMUCPUState *s); typedef struct QEMU_Elf { +#ifndef CONFIG_LINUX GMappedFile *gmf; +#endif size_t size; void *map; QEMUCPUState **state; From 231f6a7d66254a58bedbee458591b780e0a507b1 Mon Sep 17 00:00:00 2001 From: Viktor Prutyanov Date: Fri, 15 Sep 2023 20:01:53 +0300 Subject: [PATCH 33/80] elf2dmp: rework PDB_STREAM_INDEXES::segments obtaining PDB for Windows 11 kernel has slightly different structure compared to previous versions. Since elf2dmp don't use the other fields, copy only 'segments' field from PDB_STREAM_INDEXES. Signed-off-by: Viktor Prutyanov Reviewed-by: Akihiko Odaki Message-id: 20230915170153.10959-6-viktor@daynix.com Signed-off-by: Peter Maydell --- contrib/elf2dmp/pdb.c | 15 ++++----------- contrib/elf2dmp/pdb.h | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c index adcfa7e154..6ca5086f02 100644 --- a/contrib/elf2dmp/pdb.c +++ b/contrib/elf2dmp/pdb.c @@ -160,7 +160,7 @@ static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number) static int pdb_init_segments(struct pdb_reader *r) { char *segs; - unsigned stream_idx = r->sidx.segments; + unsigned stream_idx = r->segments; segs = pdb_ds_read_file(r, stream_idx); if (!segs) { @@ -177,9 +177,6 @@ static int pdb_init_symbols(struct pdb_reader *r) { int err = 0; PDB_SYMBOLS *symbols; - PDB_STREAM_INDEXES *sidx = &r->sidx; - - memset(sidx, -1, sizeof(*sidx)); symbols = pdb_ds_read_file(r, 3); if (!symbols) { @@ -188,15 +185,11 @@ static int pdb_init_symbols(struct pdb_reader *r) r->symbols = symbols; - if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) { - err = 1; - goto out_symbols; - } - - memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) + + r->segments = *(uint16_t *)((const char *)symbols + sizeof(PDB_SYMBOLS) + symbols->module_size + symbols->offset_size + symbols->hash_size + symbols->srcmodule_size + - symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx)); + symbols->pdbimport_size + symbols->unknown2_size + + offsetof(PDB_STREAM_INDEXES, segments)); /* Read global symbol table */ r->modimage = pdb_ds_read_file(r, symbols->gsym_file); diff --git a/contrib/elf2dmp/pdb.h b/contrib/elf2dmp/pdb.h index 4ea8925ee8..2a50da56ac 100644 --- a/contrib/elf2dmp/pdb.h +++ b/contrib/elf2dmp/pdb.h @@ -227,7 +227,7 @@ struct pdb_reader { } ds; uint32_t file_used[1024]; PDB_SYMBOLS *symbols; - PDB_STREAM_INDEXES sidx; + uint16_t segments; uint8_t *modimage; char *segs; size_t segs_size; From 38e476e88e7d5294c194d36d33e12f3d936a7e6a Mon Sep 17 00:00:00 2001 From: Martin Kletzander Date: Mon, 25 Apr 2022 10:21:45 +0200 Subject: [PATCH 34/80] hw/input/tsc210x: Extract common init code into new function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This deduplicates several lines and will make future changes more concise. Signed-off-by: Martin Kletzander Reviewed-by: Daniel P. Berrangé Message-ID: <1d75877cf4cc2a38f87633ff16f9fea3e1bb0c03.1650874791.git.mkletzan@redhat.com> Signed-off-by: Paolo Bonzini --- hw/input/tsc210x.c | 68 ++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c index 7eae5989f7..f568759e05 100644 --- a/hw/input/tsc210x.c +++ b/hw/input/tsc210x.c @@ -30,6 +30,7 @@ #include "hw/input/tsc2xxx.h" #include "hw/irq.h" #include "migration/vmstate.h" +#include "qapi/error.h" #define TSC_DATA_REGISTERS_PAGE 0x0 #define TSC_CONTROL_REGISTERS_PAGE 0x1 @@ -1069,20 +1070,10 @@ static const VMStateDescription vmstate_tsc2301 = { .fields = vmstatefields_tsc210x, }; -uWireSlave *tsc2102_init(qemu_irq pint) +static void tsc210x_init(TSC210xState *s, + const char *name, + const VMStateDescription *vmsd) { - TSC210xState *s; - - s = g_new0(TSC210xState, 1); - s->x = 160; - s->y = 160; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc210x_timer_tick, s); - s->pint = pint; - s->model = 0x2102; - s->name = "tsc2102"; - s->tr[0] = 0; s->tr[1] = 1; s->tr[2] = 1; @@ -1104,13 +1095,29 @@ uWireSlave *tsc2102_init(qemu_irq pint) tsc210x_reset(s); - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2102-driven Touchscreen"); + qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, name); AUD_register_card(s->name, &s->card); qemu_register_reset((void *) tsc210x_reset, s); - vmstate_register(NULL, 0, &vmstate_tsc2102, s); + vmstate_register(NULL, 0, vmsd, s); +} + +uWireSlave *tsc2102_init(qemu_irq pint) +{ + TSC210xState *s; + + s = g_new0(TSC210xState, 1); + s->x = 160; + s->y = 160; + s->pressure = 0; + s->precision = s->nextprecision = 0; + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc210x_timer_tick, s); + s->pint = pint; + s->model = 0x2102; + s->name = "tsc2102"; + + tsc210x_init(s, "QEMU TSC2102-driven Touchscreen", &vmstate_tsc2102); return &s->chip; } @@ -1131,34 +1138,7 @@ uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav) s->model = 0x2301; s->name = "tsc2301"; - s->tr[0] = 0; - s->tr[1] = 1; - s->tr[2] = 1; - s->tr[3] = 0; - s->tr[4] = 1; - s->tr[5] = 0; - s->tr[6] = 1; - s->tr[7] = 0; - - s->chip.opaque = s; - s->chip.send = (void *) tsc210x_write; - s->chip.receive = (void *) tsc210x_read; - - s->codec.opaque = s; - s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; - s->codec.set_rate = (void *) tsc210x_i2s_set_rate; - s->codec.in.fifo = s->in_fifo; - s->codec.out.fifo = s->out_fifo; - - tsc210x_reset(s); - - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2301-driven Touchscreen"); - - AUD_register_card(s->name, &s->card); - - qemu_register_reset((void *) tsc210x_reset, s); - vmstate_register(NULL, 0, &vmstate_tsc2301, s); + tsc210x_init(s, "QEMU TSC2301-driven Touchscreen", &vmstate_tsc2301); return &s->chip; } From b7639b7dd05de6d4f5ac2a30bb4a7a789286992f Mon Sep 17 00:00:00 2001 From: Martin Kletzander Date: Mon, 25 Apr 2022 10:21:46 +0200 Subject: [PATCH 35/80] hw/audio: Simplify hda audio init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No return values are used anywhere, so switch the functions to be void and add support for error reporting using errp for use in next patches. Signed-off-by: Martin Kletzander Reviewed-by: Daniel P. Berrangé Message-ID: Signed-off-by: Paolo Bonzini --- hw/audio/hda-codec.c | 32 ++++++++++++++++++-------------- hw/audio/intel-hda.c | 4 +--- hw/audio/intel-hda.h | 2 +- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index c51d8ba617..a26048cf15 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -675,7 +675,9 @@ static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, b } } -static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) +static void hda_audio_init(HDACodecDevice *hda, + const struct desc_codec *desc, + Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); HDAAudioStream *st; @@ -718,7 +720,6 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) break; } } - return 0; } static void hda_audio_exit(HDACodecDevice *hda) @@ -848,37 +849,40 @@ static Property hda_audio_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int hda_audio_init_output(HDACodecDevice *hda) +static void hda_audio_init_output(HDACodecDevice *hda, Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); + const struct desc_codec *desc = &output_nomixemu; if (!a->mixer) { - return hda_audio_init(hda, &output_nomixemu); - } else { - return hda_audio_init(hda, &output_mixemu); + desc = &output_mixemu; } + + hda_audio_init(hda, desc, errp); } -static int hda_audio_init_duplex(HDACodecDevice *hda) +static void hda_audio_init_duplex(HDACodecDevice *hda, Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); + const struct desc_codec *desc = &duplex_nomixemu; if (!a->mixer) { - return hda_audio_init(hda, &duplex_nomixemu); - } else { - return hda_audio_init(hda, &duplex_mixemu); + desc = &duplex_mixemu; } + + hda_audio_init(hda, desc, errp); } -static int hda_audio_init_micro(HDACodecDevice *hda) +static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); + const struct desc_codec *desc = µ_nomixemu; if (!a->mixer) { - return hda_audio_init(hda, µ_nomixemu); - } else { - return hda_audio_init(hda, µ_mixemu); + desc = µ_mixemu; } + + hda_audio_init(hda, desc, errp); } static void hda_audio_base_class_init(ObjectClass *klass, void *data) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index b9ed231fe8..78ff9f9a68 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -71,9 +71,7 @@ static void hda_codec_dev_realize(DeviceState *qdev, Error **errp) return; } bus->next_cad = dev->cad + 1; - if (cdc->init(dev) != 0) { - error_setg(errp, "HDA audio init failed"); - } + cdc->init(dev, errp); } static void hda_codec_dev_unrealize(DeviceState *qdev) diff --git a/hw/audio/intel-hda.h b/hw/audio/intel-hda.h index f78c1833e3..8d710eee5d 100644 --- a/hw/audio/intel-hda.h +++ b/hw/audio/intel-hda.h @@ -31,7 +31,7 @@ struct HDACodecBus { struct HDACodecDeviceClass { DeviceClass parent_class; - int (*init)(HDACodecDevice *dev); + void (*init)(HDACodecDevice *dev, Error **errp); void (*exit)(HDACodecDevice *dev); void (*command)(HDACodecDevice *dev, uint32_t nid, uint32_t data); void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running, bool output); From 79d3e56c2071d402fd55f135aea872d8fe24d269 Mon Sep 17 00:00:00 2001 From: Martin Kletzander Date: Mon, 25 Apr 2022 10:21:47 +0200 Subject: [PATCH 36/80] hw/audio/lm4549: Add errp error reporting to init function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be used in future commit. Signed-off-by: Martin Kletzander Reviewed-by: Daniel P. Berrangé Message-ID: Signed-off-by: Paolo Bonzini --- hw/audio/lm4549.c | 3 ++- hw/audio/lm4549.h | 3 ++- hw/audio/pl041.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c index 32b1481b56..418041bc9c 100644 --- a/hw/audio/lm4549.c +++ b/hw/audio/lm4549.c @@ -276,7 +276,8 @@ static int lm4549_post_load(void *opaque, int version_id) return 0; } -void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) +void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque, + Error **errp) { struct audsettings as; diff --git a/hw/audio/lm4549.h b/hw/audio/lm4549.h index aba9bb5b07..61c3ab12dd 100644 --- a/hw/audio/lm4549.h +++ b/hw/audio/lm4549.h @@ -36,7 +36,8 @@ typedef struct { extern const VMStateDescription vmstate_lm4549_state; -void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque); +void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque, + Error **errp); uint32_t lm4549_read(lm4549_state *s, hwaddr offset); void lm4549_write(lm4549_state *s, hwaddr offset, uint32_t value); uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right); diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c index 03acd4fe34..868dffbfd3 100644 --- a/hw/audio/pl041.c +++ b/hw/audio/pl041.c @@ -564,7 +564,7 @@ static void pl041_realize(DeviceState *dev, Error **errp) } /* Init the codec */ - lm4549_init(&s->codec, &pl041_request_data, (void *)s); + lm4549_init(&s->codec, &pl041_request_data, (void *)s, errp); } static const VMStateDescription vmstate_pl041_regfile = { From fac7e497ca3ee088330b19b915ed09e67fcfce4c Mon Sep 17 00:00:00 2001 From: Martin Kletzander Date: Mon, 25 Apr 2022 10:21:52 +0200 Subject: [PATCH 37/80] hw/display/xlnx_dp.c: Add audiodev property There was no way to set this and we need that for it to be able to properly initialise. Signed-off-by: Martin Kletzander Message-ID: <16963256573fcbfa7720aa2fd000ba74a4055222.1650874791.git.mkletzan@redhat.com> Signed-off-by: Paolo Bonzini --- hw/display/xlnx_dp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 43c7dd8e9c..341e91e886 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1385,6 +1385,11 @@ static void xlnx_dp_reset(DeviceState *dev) xlnx_dp_update_irq(s); } +static Property xlnx_dp_device_properties[] = { + DEFINE_AUDIO_PROPERTIES(XlnxDPState, aud_card), + DEFINE_PROP_END_OF_LIST(), +}; + static void xlnx_dp_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -1392,6 +1397,7 @@ static void xlnx_dp_class_init(ObjectClass *oc, void *data) dc->realize = xlnx_dp_realize; dc->vmsd = &vmstate_dp; dc->reset = xlnx_dp_reset; + device_class_set_props(dc, xlnx_dp_device_properties); } static const TypeInfo xlnx_dp_info = { From 50333482e16939cfeaed722e22f7b0798e5b3821 Mon Sep 17 00:00:00 2001 From: Martin Kletzander Date: Mon, 25 Apr 2022 10:21:48 +0200 Subject: [PATCH 38/80] tests/qtest: Specify audiodev= and -audiodev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will enable removing deprecated default audiodev support. I did not figure out how to make the audiodev represented as an interface node, so this is a workaround. I am not sure what would be the proper way. Signed-off-by: Martin Kletzander Reviewed-by: Daniel P. Berrangé Message-ID: <6e7f2808dd40679a415812767b88f2a411fc137f.1650874791.git.mkletzan@redhat.com> Signed-off-by: Paolo Bonzini --- tests/qtest/es1370-test.c | 3 ++- tests/qtest/fuzz/generic_fuzz_configs.h | 6 ++++-- tests/qtest/intel-hda-test.c | 15 ++++++++++----- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/qtest/es1370-test.c b/tests/qtest/es1370-test.c index 97ab65c435..8387e74193 100644 --- a/tests/qtest/es1370-test.c +++ b/tests/qtest/es1370-test.c @@ -46,7 +46,8 @@ static void *es1370_create(void *pci_bus, QGuestAllocator *alloc, void *addr) static void es1370_register_nodes(void) { QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", + .extra_device_opts = "addr=04.0,audiodev=audio0", + .before_cmd_line = "-audiodev driver=none,id=audio0", }; add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h index 50689da653..4d7c8ca4ec 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -106,8 +106,10 @@ const generic_fuzz_config predefined_configs[] = { },{ .name = "intel-hda", .args = "-machine q35 -nodefaults -device intel-hda,id=hda0 " - "-device hda-output,bus=hda0.0 -device hda-micro,bus=hda0.0 " - "-device hda-duplex,bus=hda0.0", + "-audiodev driver=none,id=audio0", + "-device hda-output,bus=hda0.0,audiodev=audio0 " + "-device hda-micro,bus=hda0.0,audiodev=audio0 " + "-device hda-duplex,bus=hda0.0,audiodev=audio0", .objects = "intel-hda", },{ .name = "ide-hd", diff --git a/tests/qtest/intel-hda-test.c b/tests/qtest/intel-hda-test.c index d4a8db6fd6..663bb6c485 100644 --- a/tests/qtest/intel-hda-test.c +++ b/tests/qtest/intel-hda-test.c @@ -11,20 +11,24 @@ #include "libqtest-single.h" #define HDA_ID "hda0" -#define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0" \ - " -device hda-micro,bus=" HDA_ID ".0" \ - " -device hda-duplex,bus=" HDA_ID ".0" +#define AUDIODEV " -audiodev driver=none,id=audio0 " +#define AUDIODEV_REF "audiodev=audio0" +#define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0," AUDIODEV_REF \ + " -device hda-micro,bus=" HDA_ID ".0," AUDIODEV_REF \ + " -device hda-duplex,bus=" HDA_ID ".0," AUDIODEV_REF /* Tests only initialization so far. TODO: Replace with functional tests */ static void ich6_test(void) { - qtest_start("-machine pc -device intel-hda,id=" HDA_ID CODEC_DEVICES); + qtest_start(AUDIODEV "-machine pc -device intel-hda,id=" HDA_ID CODEC_DEVICES); qtest_end(); } static void ich9_test(void) { - qtest_start("-machine q35 -device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=" + qtest_start("-machine q35" + AUDIODEV + "-device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=" HDA_ID CODEC_DEVICES); qtest_end(); } @@ -39,6 +43,7 @@ static void test_issue542_ich6(void) QTestState *s; s = qtest_init("-nographic -nodefaults -M pc-q35-6.2 " + AUDIODEV "-device intel-hda,id=" HDA_ID CODEC_DEVICES); qtest_outl(s, 0xcf8, 0x80000804); From adf7f6b72fb6d10e00e93d04dfa33ce8c5e384c8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 21 Sep 2023 14:04:39 +0200 Subject: [PATCH 39/80] vl: recognize audiodev groups in configuration files This is necessary for the q35 configuration tests to pass, once audiodev becomes mandatory. Signed-off-by: Paolo Bonzini --- docs/config/q35-emulated.cfg | 4 ++++ docs/config/q35-virtio-graphical.cfg | 4 ++++ softmmu/vl.c | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/docs/config/q35-emulated.cfg b/docs/config/q35-emulated.cfg index c8806e6d36..b4bd7e858a 100644 --- a/docs/config/q35-emulated.cfg +++ b/docs/config/q35-emulated.cfg @@ -288,3 +288,7 @@ driver = "hda-duplex" bus = "ich9-hda-audio.0" cad = "0" + audiodev = "audiodev0" + +[audiodev "audiodev0"] + driver = "none" # CHANGE ME diff --git a/docs/config/q35-virtio-graphical.cfg b/docs/config/q35-virtio-graphical.cfg index 148b5d2c5e..820860aefe 100644 --- a/docs/config/q35-virtio-graphical.cfg +++ b/docs/config/q35-virtio-graphical.cfg @@ -248,3 +248,7 @@ driver = "hda-duplex" bus = "sound.0" cad = "0" + audiodev = "audiodev0" + +[audiodev "audiodev0"] + driver = "none" # CHANGE ME diff --git a/softmmu/vl.c b/softmmu/vl.c index 3db4fd2680..db04f98c36 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2125,6 +2125,7 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp) static bool is_qemuopts_group(const char *group) { if (g_str_equal(group, "object") || + g_str_equal(group, "audiodev") || g_str_equal(group, "machine") || g_str_equal(group, "smp-opts") || g_str_equal(group, "boot-opts")) { @@ -2140,6 +2141,15 @@ static void qemu_record_config_group(const char *group, QDict *dict, Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(dict)); object_option_add_visitor(v); visit_free(v); + + } else if (g_str_equal(group, "audiodev")) { + Audiodev *dev = NULL; + Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(dict)); + if (visit_type_Audiodev(v, NULL, &dev, errp)) { + audio_define(dev); + } + visit_free(v); + } else if (g_str_equal(group, "machine")) { /* * Cannot merge string-valued and type-safe dictionaries, so JSON From 0189c279affc359d3d4dc6031241e24923959e7d Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 6 Sep 2023 16:09:15 +0200 Subject: [PATCH 40/80] iotests: use TEST_IMG_FILE instead of TEST_IMG in _require_large_file We need to check that we are able to create large enough file which is used as an export base rather than connection URL. Unfortunately, there are cases when the TEST_IMG_FILE is not defined. We should fallback to TEST_IMG in that case. This problem has been detected when running ./check -nbd 5 The test should be able to run while it does not. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy Message-ID: <20230906140917.559129-2-den@openvz.org> Tested-by: Eric Blake Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- tests/qemu-iotests/common.rc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index d145f08201..95c12577dd 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -979,10 +979,15 @@ _require_drivers() # _require_large_file() { - if ! truncate --size="$1" "$TEST_IMG"; then + if [ -z "$TEST_IMG_FILE" ]; then + FILENAME="$TEST_IMG" + else + FILENAME="$TEST_IMG_FILE" + fi + if ! truncate --size="$1" "$FILENAME"; then _notrun "file system on $TEST_DIR does not support large enough files" fi - rm "$TEST_IMG" + rm "$FILENAME" } # Check that a set of devices is available in the QEMU binary From 71a5655a35f7beca8ce0d3e2932cc01b84303994 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 6 Sep 2023 16:09:16 +0200 Subject: [PATCH 41/80] iotests: improve 'not run' message for nbd-multiconn test The test actually requires Python bindings to libnbd rather than libnbd itself. Clarify that inside the message. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy Message-ID: <20230906140917.559129-3-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- tests/qemu-iotests/tests/nbd-multiconn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/tests/nbd-multiconn b/tests/qemu-iotests/tests/nbd-multiconn index b121f2e363..478a1eaba2 100755 --- a/tests/qemu-iotests/tests/nbd-multiconn +++ b/tests/qemu-iotests/tests/nbd-multiconn @@ -142,4 +142,4 @@ if __name__ == '__main__': iotests.main(supported_fmts=['qcow2']) except ImportError: - iotests.notrun('libnbd not installed') + iotests.notrun('Python bindings to libnbd are not installed') From ac132d0520c39f48a22e8666810792c5c9dd44b1 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 29 Aug 2023 12:58:28 -0500 Subject: [PATCH 42/80] nbd: Replace bool structured_reply with mode enum The upcoming patches for 64-bit extensions requires various points in the protocol to make decisions based on what was negotiated. While we could easily add a 'bool extended_headers' alongside the existing 'bool structured_reply', this does not scale well if more modes are added in the future. Better is to expose the mode enum added in the recent commit bfe04d0a7d out to a wider use in the code base. Where the code previously checked for structured_reply being set or clear, it now prefers checking for an inequality; this works because the nodes are in a continuum of increasing abilities, and allows us to touch fewer places if we ever insert other modes in the middle of the enum. There should be no semantic change in this patch. Signed-off-by: Eric Blake Message-ID: <20230829175826.377251-20-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 8 +++++--- include/block/nbd.h | 2 +- nbd/client-connection.c | 4 ++-- nbd/client.c | 18 +++++++++--------- nbd/server.c | 31 ++++++++++++++++++------------- qemu-nbd.c | 4 +++- 6 files changed, 38 insertions(+), 29 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index cc48580df7..676b755d79 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -463,7 +463,8 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie) nbd_channel_error(s, ret); return ret; } - if (nbd_reply_is_structured(&s->reply) && !s->info.structured_reply) { + if (nbd_reply_is_structured(&s->reply) && + s->info.mode < NBD_MODE_STRUCTURED) { nbd_channel_error(s, -EINVAL); return -EINVAL; } @@ -866,7 +867,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } /* handle structured reply chunk */ - assert(s->info.structured_reply); + assert(s->info.mode >= NBD_MODE_STRUCTURED); chunk = &s->reply.structured; if (chunk->type == NBD_REPLY_TYPE_NONE) { @@ -1070,7 +1071,8 @@ nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t cookie, void *payload = NULL; Error *local_err = NULL; - NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, s->info.structured_reply, + NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, + s->info.mode >= NBD_MODE_STRUCTURED, qiov, &reply, &payload) { int ret; diff --git a/include/block/nbd.h b/include/block/nbd.h index f672b76173..5322676457 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -305,7 +305,7 @@ typedef struct NBDExportInfo { /* In-out fields, set by client before nbd_receive_negotiate() and * updated by server results during nbd_receive_negotiate() */ - bool structured_reply; + NBDMode mode; /* input maximum mode tolerated; output actual mode chosen */ bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ /* Set by server results during nbd_receive_negotiate() and diff --git a/nbd/client-connection.c b/nbd/client-connection.c index 53a6549914..aa0201b710 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -1,5 +1,5 @@ /* - * QEMU Block driver for NBD + * QEMU Block driver for NBD * * Copyright (c) 2021 Virtuozzo International GmbH. * @@ -93,7 +93,7 @@ NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, .do_negotiation = do_negotiation, .initial_info.request_sizes = true, - .initial_info.structured_reply = true, + .initial_info.mode = NBD_MODE_STRUCTURED, .initial_info.base_allocation = true, .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap), .initial_info.name = g_strdup(export_name ?: "") diff --git a/nbd/client.c b/nbd/client.c index bd7e200136..844be42181 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -879,7 +879,7 @@ static int nbd_list_meta_contexts(QIOChannel *ioc, */ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, const char *hostname, QIOChannel **outioc, - bool structured_reply, bool *zeroes, + NBDMode max_mode, bool *zeroes, Error **errp) { ERRP_GUARD(); @@ -953,7 +953,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, if (fixedNewStyle) { int result = 0; - if (structured_reply) { + if (max_mode >= NBD_MODE_STRUCTURED) { result = nbd_request_simple_option(ioc, NBD_OPT_STRUCTURED_REPLY, false, errp); @@ -1022,20 +1022,19 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, trace_nbd_receive_negotiate_name(info->name); result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, - info->structured_reply, &zeroes, errp); + info->mode, &zeroes, errp); if (result < 0) { return result; } - info->structured_reply = false; + info->mode = result; info->base_allocation = false; if (tlscreds && *outioc) { ioc = *outioc; } - switch ((NBDMode)result) { + switch (info->mode) { case NBD_MODE_STRUCTURED: - info->structured_reply = true; if (base_allocation) { result = nbd_negotiate_simple_meta_context(ioc, info, errp); if (result < 0) { @@ -1144,8 +1143,8 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, QIOChannel *sioc = NULL; *info = NULL; - result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, true, - NULL, errp); + result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, + NBD_MODE_STRUCTURED, NULL, errp); if (tlscreds && sioc) { ioc = sioc; } @@ -1176,7 +1175,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, memset(&array[count - 1], 0, sizeof(*array)); array[count - 1].name = name; array[count - 1].description = desc; - array[count - 1].structured_reply = result == NBD_MODE_STRUCTURED; + array[count - 1].mode = result; } for (i = 0; i < count; i++) { @@ -1209,6 +1208,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, /* Lone export name is implied, but we can parse length and flags */ array = g_new0(NBDExportInfo, 1); array->name = g_strdup(""); + array->mode = NBD_MODE_OLDSTYLE; count = 1; if (nbd_negotiate_finish_oldstyle(ioc, array, errp) < 0) { diff --git a/nbd/server.c b/nbd/server.c index b5f93a20c9..936c35e55c 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -143,7 +143,7 @@ struct NBDClient { uint32_t check_align; /* If non-zero, check for aligned client requests */ - bool structured_reply; + NBDMode mode; NBDExportMetaContexts export_meta; uint32_t opt; /* Current option being negotiated */ @@ -502,7 +502,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, } myflags = client->exp->nbdflags; - if (client->structured_reply) { + if (client->mode >= NBD_MODE_STRUCTURED) { myflags |= NBD_FLAG_SEND_DF; } trace_nbd_negotiate_new_style_size_flags(client->exp->size, myflags); @@ -687,7 +687,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, Error **errp) /* Send NBD_INFO_EXPORT always */ myflags = exp->nbdflags; - if (client->structured_reply) { + if (client->mode >= NBD_MODE_STRUCTURED) { myflags |= NBD_FLAG_SEND_DF; } trace_nbd_negotiate_new_style_size_flags(exp->size, myflags); @@ -985,7 +985,8 @@ static int nbd_negotiate_meta_queries(NBDClient *client, size_t i; size_t count = 0; - if (client->opt == NBD_OPT_SET_META_CONTEXT && !client->structured_reply) { + if (client->opt == NBD_OPT_SET_META_CONTEXT && + client->mode < NBD_MODE_STRUCTURED) { return nbd_opt_invalid(client, errp, "request option '%s' when structured reply " "is not negotiated", @@ -1122,10 +1123,12 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) if (nbd_read32(client->ioc, &flags, "flags", errp) < 0) { return -EIO; } + client->mode = NBD_MODE_EXPORT_NAME; trace_nbd_negotiate_options_flags(flags); if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { fixedNewstyle = true; flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; + client->mode = NBD_MODE_SIMPLE; } if (flags & NBD_FLAG_C_NO_ZEROES) { no_zeroes = true; @@ -1261,13 +1264,13 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) case NBD_OPT_STRUCTURED_REPLY: if (length) { ret = nbd_reject_length(client, false, errp); - } else if (client->structured_reply) { + } else if (client->mode >= NBD_MODE_STRUCTURED) { ret = nbd_negotiate_send_rep_err( client, NBD_REP_ERR_INVALID, errp, "structured reply already negotiated"); } else { ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); - client->structured_reply = true; + client->mode = NBD_MODE_STRUCTURED; } break; @@ -1895,7 +1898,9 @@ static int coroutine_fn nbd_co_send_simple_reply(NBDClient *client, }; assert(!len || !nbd_err); - assert(!client->structured_reply || request->type != NBD_CMD_READ); + assert(client->mode < NBD_MODE_STRUCTURED || + (client->mode == NBD_MODE_STRUCTURED && + request->type != NBD_CMD_READ)); trace_nbd_co_send_simple_reply(request->cookie, nbd_err, nbd_err_lookup(nbd_err), len); set_be_simple_reply(&reply, nbd_err, request->cookie); @@ -1971,7 +1976,7 @@ static int coroutine_fn nbd_co_send_chunk_read(NBDClient *client, return nbd_co_send_iov(client, iov, 3, errp); } -/*ebb*/ + static int coroutine_fn nbd_co_send_chunk_error(NBDClient *client, NBDRequest *request, uint32_t error, @@ -2397,7 +2402,7 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * client->check_align); } valid_flags = NBD_CMD_FLAG_FUA; - if (request->type == NBD_CMD_READ && client->structured_reply) { + if (request->type == NBD_CMD_READ && client->mode >= NBD_MODE_STRUCTURED) { valid_flags |= NBD_CMD_FLAG_DF; } else if (request->type == NBD_CMD_WRITE_ZEROES) { valid_flags |= NBD_CMD_FLAG_NO_HOLE | NBD_CMD_FLAG_FAST_ZERO; @@ -2423,7 +2428,7 @@ static coroutine_fn int nbd_send_generic_reply(NBDClient *client, const char *error_msg, Error **errp) { - if (client->structured_reply && ret < 0) { + if (client->mode >= NBD_MODE_STRUCTURED && ret < 0) { return nbd_co_send_chunk_error(client, request, -ret, error_msg, errp); } else { return nbd_co_send_simple_reply(client, request, ret < 0 ? -ret : 0, @@ -2451,8 +2456,8 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, } } - if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) && - request->len) + if (client->mode >= NBD_MODE_STRUCTURED && + !(request->flags & NBD_CMD_FLAG_DF) && request->len) { return nbd_co_send_sparse_read(client, request, request->from, data, request->len, errp); @@ -2464,7 +2469,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, "reading from file failed", errp); } - if (client->structured_reply) { + if (client->mode >= NBD_MODE_STRUCTURED) { if (request->len) { return nbd_co_send_chunk_read(client, request, request->from, data, request->len, true, errp); diff --git a/qemu-nbd.c b/qemu-nbd.c index 30eeb6f3c7..70aa3c487a 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -295,7 +295,9 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { struct NbdClientOpts *opts = arg; - NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") }; + /* TODO: Revisit this if nbd.ko ever gains support for structured reply */ + NBDExportInfo info = { .request_sizes = false, .name = g_strdup(""), + .mode = NBD_MODE_SIMPLE }; QIOChannelSocket *sioc; int fd = -1; int ret = EXIT_FAILURE; From 297365b40ff24c7e07d6f40ec8f9ac83229ace94 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 29 Aug 2023 12:58:29 -0500 Subject: [PATCH 43/80] nbd/client: Pass mode through to nbd_send_request Once the 64-bit headers extension is enabled, the data layout we send over the wire for a client request depends on the mode negotiated with the server. Rather than adding a parameter to nbd_send_request, we can add a member to struct NBDRequest, since it already does not reflect on-wire format. Some callers initialize it directly; many others rely on a common initialization point during nbd_co_send_request(). At this point, there is no semantic change. Signed-off-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20230829175826.377251-21-eblake@redhat.com> --- block/nbd.c | 5 +++-- include/block/nbd.h | 12 +++++++----- nbd/client.c | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 676b755d79..24f50b79e4 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -339,7 +339,7 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, * We have connected, but must fail for other reasons. * Send NBD_CMD_DISC as a courtesy to the server. */ - NBDRequest request = { .type = NBD_CMD_DISC }; + NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; nbd_send_request(s->ioc, &request); @@ -520,6 +520,7 @@ nbd_co_send_request(BlockDriverState *bs, NBDRequest *request, qemu_co_mutex_lock(&s->send_mutex); request->cookie = INDEX_TO_COOKIE(i); + request->mode = s->info.mode; assert(s->ioc); @@ -1465,7 +1466,7 @@ static void nbd_yank(void *opaque) static void nbd_client_close(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - NBDRequest request = { .type = NBD_CMD_DISC }; + NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; if (s->ioc) { nbd_send_request(s->ioc, &request); diff --git a/include/block/nbd.h b/include/block/nbd.h index 5322676457..e07b9f9bff 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -63,17 +63,19 @@ typedef enum NBDMode { /* TODO add NBD_MODE_EXTENDED */ } NBDMode; -/* Transmission phase structs - * - * Note: these are _NOT_ the same as the network representation of an NBD - * request and reply! +/* Transmission phase structs */ + +/* + * Note: NBDRequest is _NOT_ the same as the network representation of an NBD + * request! */ typedef struct NBDRequest { uint64_t cookie; uint64_t from; uint32_t len; uint16_t flags; /* NBD_CMD_FLAG_* */ - uint16_t type; /* NBD_CMD_* */ + uint16_t type; /* NBD_CMD_* */ + NBDMode mode; /* Determines which network representation to use */ } NBDRequest; typedef struct NBDSimpleReply { diff --git a/nbd/client.c b/nbd/client.c index 844be42181..345f1c0f2d 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1218,7 +1218,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, /* Send NBD_CMD_DISC as a courtesy to the server, but ignore all * errors now that we have the information we wanted. */ if (nbd_drop(ioc, 124, NULL) == 0) { - NBDRequest request = { .type = NBD_CMD_DISC }; + NBDRequest request = { .type = NBD_CMD_DISC, .mode = result }; nbd_send_request(ioc, &request); } @@ -1348,6 +1348,7 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request) { uint8_t buf[NBD_REQUEST_SIZE]; + assert(request->mode <= NBD_MODE_STRUCTURED); /* TODO handle extended */ trace_nbd_send_request(request->from, request->len, request->cookie, request->flags, request->type, nbd_cmd_lookup(request->type)); From d95ffb6fe6008c114602e20d848100081d62f7aa Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 29 Aug 2023 12:58:30 -0500 Subject: [PATCH 44/80] nbd: Add types for extended headers Add the constants and structs necessary for later patches to start implementing the NBD_OPT_EXTENDED_HEADERS extension in both the client and server, matching recent upstream nbd.git (through commit e6f3b94a934). This patch does not change any existing behavior, but merely sets the stage for upcoming patches. This patch does not change the status quo that neither the client nor server use a packed-struct representation for the request header. While most of the patch adds new types, there is also some churn for renaming the existing NBDExtent to NBDExtent32 to contrast it with NBDExtent64, which I thought was a nicer name than NBDExtentExt. Signed-off-by: Eric Blake Reviewed-by: Vladimir Sementsov-Ogievskiy Message-ID: <20230829175826.377251-22-eblake@redhat.com> --- block/nbd.c | 6 +-- include/block/nbd.h | 124 +++++++++++++++++++++++++++++++------------- nbd/common.c | 12 ++++- nbd/nbd-internal.h | 3 +- nbd/server.c | 6 +-- 5 files changed, 106 insertions(+), 45 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 24f50b79e4..9e99a4ddb5 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -610,7 +610,7 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s, static int nbd_parse_blockstatus_payload(BDRVNBDState *s, NBDStructuredReplyChunk *chunk, uint8_t *payload, uint64_t orig_length, - NBDExtent *extent, Error **errp) + NBDExtent32 *extent, Error **errp) { uint32_t context_id; @@ -1118,7 +1118,7 @@ nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t cookie, static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, - uint64_t length, NBDExtent *extent, + uint64_t length, NBDExtent32 *extent, int *request_ret, Error **errp) { NBDReplyChunkIter iter; @@ -1373,7 +1373,7 @@ static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( int64_t *pnum, int64_t *map, BlockDriverState **file) { int ret, request_ret; - NBDExtent extent = { 0 }; + NBDExtent32 extent = { 0 }; BDRVNBDState *s = (BDRVNBDState *)bs->opaque; Error *local_err = NULL; diff --git a/include/block/nbd.h b/include/block/nbd.h index e07b9f9bff..22a9b5d10e 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -60,7 +60,7 @@ typedef enum NBDMode { NBD_MODE_EXPORT_NAME, /* newstyle but only OPT_EXPORT_NAME safe */ NBD_MODE_SIMPLE, /* newstyle but only simple replies */ NBD_MODE_STRUCTURED, /* newstyle, structured replies enabled */ - /* TODO add NBD_MODE_EXTENDED */ + NBD_MODE_EXTENDED, /* newstyle, extended headers enabled */ } NBDMode; /* Transmission phase structs */ @@ -93,20 +93,36 @@ typedef struct NBDStructuredReplyChunk { uint32_t length; /* length of payload */ } QEMU_PACKED NBDStructuredReplyChunk; +typedef struct NBDExtendedReplyChunk { + uint32_t magic; /* NBD_EXTENDED_REPLY_MAGIC */ + uint16_t flags; /* combination of NBD_REPLY_FLAG_* */ + uint16_t type; /* NBD_REPLY_TYPE_* */ + uint64_t cookie; /* request handle */ + uint64_t offset; /* request offset */ + uint64_t length; /* length of payload */ +} QEMU_PACKED NBDExtendedReplyChunk; + typedef union NBDReply { NBDSimpleReply simple; NBDStructuredReplyChunk structured; + NBDExtendedReplyChunk extended; struct { /* - * @magic and @cookie fields have the same offset and size both in - * simple reply and structured reply chunk, so let them be accessible - * without ".simple." or ".structured." specification + * @magic and @cookie fields have the same offset and size in all + * forms of replies, so let them be accessible without ".simple.", + * ".structured.", or ".extended." specifications. */ uint32_t magic; uint32_t _skip; uint64_t cookie; - } QEMU_PACKED; + }; } NBDReply; +QEMU_BUILD_BUG_ON(offsetof(NBDReply, simple.cookie) != + offsetof(NBDReply, cookie)); +QEMU_BUILD_BUG_ON(offsetof(NBDReply, structured.cookie) != + offsetof(NBDReply, cookie)); +QEMU_BUILD_BUG_ON(offsetof(NBDReply, extended.cookie) != + offsetof(NBDReply, cookie)); /* Header of chunk for NBD_REPLY_TYPE_OFFSET_DATA */ typedef struct NBDStructuredReadData { @@ -133,14 +149,34 @@ typedef struct NBDStructuredError { typedef struct NBDStructuredMeta { /* header's length >= 12 (at least one extent) */ uint32_t context_id; - /* extents follows */ + /* NBDExtent32 extents[] follows, array length implied by header */ } QEMU_PACKED NBDStructuredMeta; -/* Extent chunk for NBD_REPLY_TYPE_BLOCK_STATUS */ -typedef struct NBDExtent { +/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS */ +typedef struct NBDExtent32 { uint32_t length; uint32_t flags; /* NBD_STATE_* */ -} QEMU_PACKED NBDExtent; +} QEMU_PACKED NBDExtent32; + +/* Header of NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ +typedef struct NBDExtendedMeta { + /* header's length >= 24 (at least one extent) */ + uint32_t context_id; + uint32_t count; /* header length must be count * 16 + 8 */ + /* NBDExtent64 extents[count] follows */ +} QEMU_PACKED NBDExtendedMeta; + +/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ +typedef struct NBDExtent64 { + uint64_t length; + uint64_t flags; /* NBD_STATE_* */ +} QEMU_PACKED NBDExtent64; + +/* Client payload for limiting NBD_CMD_BLOCK_STATUS reply */ +typedef struct NBDBlockStatusPayload { + uint64_t effect_length; + /* uint32_t ids[] follows, array length implied by header */ +} QEMU_PACKED NBDBlockStatusPayload; /* Transmission (export) flags: sent from server to client during handshake, but describe what will happen during transmission */ @@ -158,20 +194,22 @@ enum { NBD_FLAG_SEND_RESIZE_BIT = 9, /* Send resize */ NBD_FLAG_SEND_CACHE_BIT = 10, /* Send CACHE (prefetch) */ NBD_FLAG_SEND_FAST_ZERO_BIT = 11, /* FAST_ZERO flag for WRITE_ZEROES */ + NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT = 12, /* PAYLOAD flag for BLOCK_STATUS */ }; -#define NBD_FLAG_HAS_FLAGS (1 << NBD_FLAG_HAS_FLAGS_BIT) -#define NBD_FLAG_READ_ONLY (1 << NBD_FLAG_READ_ONLY_BIT) -#define NBD_FLAG_SEND_FLUSH (1 << NBD_FLAG_SEND_FLUSH_BIT) -#define NBD_FLAG_SEND_FUA (1 << NBD_FLAG_SEND_FUA_BIT) -#define NBD_FLAG_ROTATIONAL (1 << NBD_FLAG_ROTATIONAL_BIT) -#define NBD_FLAG_SEND_TRIM (1 << NBD_FLAG_SEND_TRIM_BIT) -#define NBD_FLAG_SEND_WRITE_ZEROES (1 << NBD_FLAG_SEND_WRITE_ZEROES_BIT) -#define NBD_FLAG_SEND_DF (1 << NBD_FLAG_SEND_DF_BIT) -#define NBD_FLAG_CAN_MULTI_CONN (1 << NBD_FLAG_CAN_MULTI_CONN_BIT) -#define NBD_FLAG_SEND_RESIZE (1 << NBD_FLAG_SEND_RESIZE_BIT) -#define NBD_FLAG_SEND_CACHE (1 << NBD_FLAG_SEND_CACHE_BIT) -#define NBD_FLAG_SEND_FAST_ZERO (1 << NBD_FLAG_SEND_FAST_ZERO_BIT) +#define NBD_FLAG_HAS_FLAGS (1 << NBD_FLAG_HAS_FLAGS_BIT) +#define NBD_FLAG_READ_ONLY (1 << NBD_FLAG_READ_ONLY_BIT) +#define NBD_FLAG_SEND_FLUSH (1 << NBD_FLAG_SEND_FLUSH_BIT) +#define NBD_FLAG_SEND_FUA (1 << NBD_FLAG_SEND_FUA_BIT) +#define NBD_FLAG_ROTATIONAL (1 << NBD_FLAG_ROTATIONAL_BIT) +#define NBD_FLAG_SEND_TRIM (1 << NBD_FLAG_SEND_TRIM_BIT) +#define NBD_FLAG_SEND_WRITE_ZEROES (1 << NBD_FLAG_SEND_WRITE_ZEROES_BIT) +#define NBD_FLAG_SEND_DF (1 << NBD_FLAG_SEND_DF_BIT) +#define NBD_FLAG_CAN_MULTI_CONN (1 << NBD_FLAG_CAN_MULTI_CONN_BIT) +#define NBD_FLAG_SEND_RESIZE (1 << NBD_FLAG_SEND_RESIZE_BIT) +#define NBD_FLAG_SEND_CACHE (1 << NBD_FLAG_SEND_CACHE_BIT) +#define NBD_FLAG_SEND_FAST_ZERO (1 << NBD_FLAG_SEND_FAST_ZERO_BIT) +#define NBD_FLAG_BLOCK_STAT_PAYLOAD (1 << NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT) /* New-style handshake (global) flags, sent from server to client, and control what will happen during handshake phase. */ @@ -194,6 +232,7 @@ enum { #define NBD_OPT_STRUCTURED_REPLY (8) #define NBD_OPT_LIST_META_CONTEXT (9) #define NBD_OPT_SET_META_CONTEXT (10) +#define NBD_OPT_EXTENDED_HEADERS (11) /* Option reply types. */ #define NBD_REP_ERR(value) ((UINT32_C(1) << 31) | (value)) @@ -211,6 +250,8 @@ enum { #define NBD_REP_ERR_UNKNOWN NBD_REP_ERR(6) /* Export unknown */ #define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ #define NBD_REP_ERR_BLOCK_SIZE_REQD NBD_REP_ERR(8) /* Need INFO_BLOCK_SIZE */ +#define NBD_REP_ERR_TOO_BIG NBD_REP_ERR(9) /* Payload size overflow */ +#define NBD_REP_ERR_EXT_HEADER_REQD NBD_REP_ERR(10) /* Need extended headers */ /* Info types, used during NBD_REP_INFO */ #define NBD_INFO_EXPORT 0 @@ -219,12 +260,14 @@ enum { #define NBD_INFO_BLOCK_SIZE 3 /* Request flags, sent from client to server during transmission phase */ -#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ -#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */ -#define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */ -#define NBD_CMD_FLAG_REQ_ONE (1 << 3) /* only one extent in BLOCK_STATUS - * reply chunk */ -#define NBD_CMD_FLAG_FAST_ZERO (1 << 4) /* fail if WRITE_ZEROES is not fast */ +#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ +#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */ +#define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */ +#define NBD_CMD_FLAG_REQ_ONE (1 << 3) \ + /* only one extent in BLOCK_STATUS reply chunk */ +#define NBD_CMD_FLAG_FAST_ZERO (1 << 4) /* fail if WRITE_ZEROES is not fast */ +#define NBD_CMD_FLAG_PAYLOAD_LEN (1 << 5) \ + /* length describes payload, not effect; only with ext header */ /* Supported request types */ enum { @@ -250,22 +293,31 @@ enum { */ #define NBD_MAX_STRING_SIZE 4096 -/* Two types of reply structures */ +/* Two types of request structures, a given client will only use 1 */ +#define NBD_REQUEST_MAGIC 0x25609513 +#define NBD_EXTENDED_REQUEST_MAGIC 0x21e41c71 + +/* + * Three types of reply structures, but what a client expects depends + * on NBD_OPT_STRUCTURED_REPLY and NBD_OPT_EXTENDED_HEADERS. + */ #define NBD_SIMPLE_REPLY_MAGIC 0x67446698 #define NBD_STRUCTURED_REPLY_MAGIC 0x668e33ef +#define NBD_EXTENDED_REPLY_MAGIC 0x6e8a278c -/* Structured reply flags */ +/* Chunk reply flags (for structured and extended replies) */ #define NBD_REPLY_FLAG_DONE (1 << 0) /* This reply-chunk is last */ -/* Structured reply types */ +/* Chunk reply types */ #define NBD_REPLY_ERR(value) ((1 << 15) | (value)) -#define NBD_REPLY_TYPE_NONE 0 -#define NBD_REPLY_TYPE_OFFSET_DATA 1 -#define NBD_REPLY_TYPE_OFFSET_HOLE 2 -#define NBD_REPLY_TYPE_BLOCK_STATUS 5 -#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) -#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) +#define NBD_REPLY_TYPE_NONE 0 +#define NBD_REPLY_TYPE_OFFSET_DATA 1 +#define NBD_REPLY_TYPE_OFFSET_HOLE 2 +#define NBD_REPLY_TYPE_BLOCK_STATUS 5 +#define NBD_REPLY_TYPE_BLOCK_STATUS_EXT 6 +#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) +#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) /* Extent flags for base:allocation in NBD_REPLY_TYPE_BLOCK_STATUS */ #define NBD_STATE_HOLE (1 << 0) diff --git a/nbd/common.c b/nbd/common.c index 989fbe54a1..3247c1d618 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -79,6 +79,8 @@ const char *nbd_opt_lookup(uint32_t opt) return "list meta context"; case NBD_OPT_SET_META_CONTEXT: return "set meta context"; + case NBD_OPT_EXTENDED_HEADERS: + return "extended headers"; default: return ""; } @@ -112,6 +114,10 @@ const char *nbd_rep_lookup(uint32_t rep) return "server shutting down"; case NBD_REP_ERR_BLOCK_SIZE_REQD: return "block size required"; + case NBD_REP_ERR_TOO_BIG: + return "option payload too big"; + case NBD_REP_ERR_EXT_HEADER_REQD: + return "extended headers required"; default: return ""; } @@ -170,7 +176,9 @@ const char *nbd_reply_type_lookup(uint16_t type) case NBD_REPLY_TYPE_OFFSET_HOLE: return "hole"; case NBD_REPLY_TYPE_BLOCK_STATUS: - return "block status"; + return "block status (32-bit)"; + case NBD_REPLY_TYPE_BLOCK_STATUS_EXT: + return "block status (64-bit)"; case NBD_REPLY_TYPE_ERROR: return "generic error"; case NBD_REPLY_TYPE_ERROR_OFFSET: @@ -261,6 +269,8 @@ const char *nbd_mode_lookup(NBDMode mode) return "simple headers"; case NBD_MODE_STRUCTURED: return "structured replies"; + case NBD_MODE_EXTENDED: + return "extended headers"; default: return ""; } diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index df42fef706..133b1d94b5 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -1,7 +1,7 @@ /* * NBD Internal Declarations * - * Copyright (C) 2016 Red Hat, Inc. + * Copyright Red Hat * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -44,7 +44,6 @@ #define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124) #define NBD_INIT_MAGIC 0x4e42444d41474943LL /* ASCII "NBDMAGIC" */ -#define NBD_REQUEST_MAGIC 0x25609513 #define NBD_OPTS_MAGIC 0x49484156454F5054LL /* ASCII "IHAVEOPT" */ #define NBD_CLIENT_MAGIC 0x0000420281861253LL #define NBD_REP_MAGIC 0x0003e889045565a9LL diff --git a/nbd/server.c b/nbd/server.c index 936c35e55c..35fcd9fdbc 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -2072,7 +2072,7 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, } typedef struct NBDExtentArray { - NBDExtent *extents; + NBDExtent32 *extents; unsigned int nb_alloc; unsigned int count; uint64_t total_length; @@ -2085,7 +2085,7 @@ static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc) NBDExtentArray *ea = g_new0(NBDExtentArray, 1); ea->nb_alloc = nb_alloc; - ea->extents = g_new(NBDExtent, nb_alloc); + ea->extents = g_new(NBDExtent32, nb_alloc); ea->can_add = true; return ea; @@ -2148,7 +2148,7 @@ static int nbd_extent_array_add(NBDExtentArray *ea, } ea->total_length += length; - ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags}; + ea->extents[ea->count] = (NBDExtent32) {.length = length, .flags = flags}; ea->count++; return 0; From c64023b0ba677cfa6b878e82ea8e18507a597396 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 24 Aug 2023 11:42:08 +0200 Subject: [PATCH 45/80] meson.build: Make keyutils independent from keyring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0db0fbb5cf ("Add conditional dependency for libkeyutils") tried to provide a possibility for the user to disable keyutils if not required by makeing it depend on the keyring feature. This looked reasonable at a first glance (the unit test in tests/unit/ needs both), but the condition in meson.build fails if the feature is meant to be detected automatically, and there is also another spot in backends/meson.build where keyutils is used independently from keyring. So let's remove the dependency on keyring again and introduce a proper meson build option instead. Cc: qemu-stable@nongnu.org Fixes: 0db0fbb5cf ("Add conditional dependency for libkeyutils") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1842 Message-ID: <20230824094208.255279-1-thuth@redhat.com> Reviewed-by: "Daniel P. Berrangé" Signed-off-by: Thomas Huth --- meson.build | 6 ++++-- meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index f426861d90..5139db2ff7 100644 --- a/meson.build +++ b/meson.build @@ -1763,8 +1763,9 @@ if gnutls.found() method: 'pkg-config') endif keyutils = not_found -if get_option('keyring').enabled() - keyutils = dependency('libkeyutils', required: false, method: 'pkg-config') +if not get_option('libkeyutils').auto() or have_block + keyutils = dependency('libkeyutils', required: get_option('libkeyutils'), + method: 'pkg-config') endif has_gettid = cc.has_function('gettid') @@ -4229,6 +4230,7 @@ endif summary_info += {'AF_ALG support': have_afalg} summary_info += {'rng-none': get_option('rng_none')} summary_info += {'Linux keyring': have_keyring} +summary_info += {'Linux keyutils': keyutils} summary(summary_info, bool_yn: true, section: 'Crypto') # UI diff --git a/meson_options.txt b/meson_options.txt index 2ca40f22e9..57e265c871 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -121,6 +121,8 @@ option('avx512bw', type: 'feature', value: 'auto', description: 'AVX512BW optimizations') option('keyring', type: 'feature', value: 'auto', description: 'Linux keyring support') +option('libkeyutils', type: 'feature', value: 'auto', + description: 'Linux keyutils support') option('af_xdp', type : 'feature', value : 'auto', description: 'AF_XDP network backend support') diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 230119346a..e4b46d5715 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -122,6 +122,7 @@ meson_options_help() { printf "%s\n" ' libdaxctl libdaxctl support' printf "%s\n" ' libdw debuginfo support' printf "%s\n" ' libiscsi libiscsi userspace initiator' + printf "%s\n" ' libkeyutils Linux keyutils support' printf "%s\n" ' libnfs libnfs block device driver' printf "%s\n" ' libpmem libpmem support' printf "%s\n" ' libssh ssh block device support' @@ -345,6 +346,8 @@ _meson_option_parse() { --libexecdir=*) quote_sh "-Dlibexecdir=$2" ;; --enable-libiscsi) printf "%s" -Dlibiscsi=enabled ;; --disable-libiscsi) printf "%s" -Dlibiscsi=disabled ;; + --enable-libkeyutils) printf "%s" -Dlibkeyutils=enabled ;; + --disable-libkeyutils) printf "%s" -Dlibkeyutils=disabled ;; --enable-libnfs) printf "%s" -Dlibnfs=enabled ;; --disable-libnfs) printf "%s" -Dlibnfs=disabled ;; --enable-libpmem) printf "%s" -Dlibpmem=enabled ;; From 0daaf2761f6d268ffaa2d01d450e202e127452b1 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 12 Sep 2023 09:33:10 -0400 Subject: [PATCH 46/80] tests/qtest/netdev-socket: Raise connection timeout to 120 seconds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test still fails intermittently with a 60 second timeout in the GitLab CI environment. Raise the timeout to 120 seconds. 576/839 ERROR:../tests/qtest/netdev-socket.c:293:test_stream_unix: assertion failed (resp == expect): ("st0: index=0,type=stream,connection error\r\n" == "st0: index=0,type=stream,unix:/tmp/netdev-socket.UW5IA2/stream_unix\r\n") ERROR 576/839 qemu:qtest+qtest-sh4 / qtest-sh4/netdev-socket ERROR 62.85s killed by signal 6 SIGABRT >>> MALLOC_PERTURB_=249 QTEST_QEMU_BINARY=./qemu-system-sh4 QTEST_QEMU_STORAGE_DAEMON_BINARY=./storage-daemon/qemu-storage-daemon G_TEST_DBUS_DAEMON=/home/gitlab-runner/builds/-LCfcJ2T/0/qemu-project/qemu/tests/dbus-vmstate-daemon.sh QTEST_QEMU_IMG=./qemu-img /home/gitlab-runner/builds/-LCfcJ2T/0/qemu-project/qemu/build/tests/qtest/netdev-socket --tap -k ――――――――――――――――――――――――――――――――――――― ✀ ――――――――――――――――――――――――――――――――――――― stderr: ** ERROR:../tests/qtest/netdev-socket.c:293:test_stream_unix: assertion failed (resp == expect): ("st0: index=0,type=stream,connection error\r\n" == "st0: index=0,type=stream,unix:/tmp/netdev-socket.UW5IA2/stream_unix\r\n") (test program exited with status code -6) Buglink: https://gitlab.com/qemu-project/qemu/-/issues/1881 Fixes: 417296c8d858 ("tests/qtest/netdev-socket: Raise connection timeout to 60 seconds") Signed-off-by: Stefan Hajnoczi Reviewed-by: Laurent Vivier Reviewed-by: "Daniel P. Berrangé" Message-ID: <20230912133310.60583-1-stefanha@redhat.com> Signed-off-by: Thomas Huth --- tests/qtest/netdev-socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index 8eed54801f..b2501d72a1 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -16,7 +16,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-sockets.h" -#define CONNECTION_TIMEOUT 60 +#define CONNECTION_TIMEOUT 120 #define EXPECT_STATE(q, e, t) \ do { \ From 926bef1d82bb9eb7a752aa128d9e70b808906243 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 22 Sep 2023 18:37:42 +0200 Subject: [PATCH 47/80] tests/qtest/m48t59-test: Silence compiler warning with -Wshadow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling this file with -Wshadow=local , we get: ../tests/qtest/m48t59-test.c: In function ‘bcd_check_time’: ../tests/qtest/m48t59-test.c:195:17: warning: declaration of ‘s’ shadows a previous local [-Wshadow=local] 195 | long t, s; | ^ ../tests/qtest/m48t59-test.c:158:17: note: shadowed declaration is here 158 | QTestState *s = m48t59_qtest_start(); | ^ Rename the QTestState variable to "qts" which is the common naming for such a variable in other tests. Reported-by: Markus Armbruster Message-ID: <20230922163742.149444-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: "Daniel P. Berrangé" Signed-off-by: Thomas Huth --- tests/qtest/m48t59-test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index 843d2ced8e..9487faff1a 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -155,7 +155,7 @@ static void bcd_check_time(void) struct tm *datep; time_t ts; const int wiggle = 2; - QTestState *s = m48t59_qtest_start(); + QTestState *qts = m48t59_qtest_start(); /* * This check assumes a few things. First, we cannot guarantee that we get @@ -173,10 +173,10 @@ static void bcd_check_time(void) ts = time(NULL); gmtime_r(&ts, &start); - cmos_get_date_time(s, &date[0]); - cmos_get_date_time(s, &date[1]); - cmos_get_date_time(s, &date[2]); - cmos_get_date_time(s, &date[3]); + cmos_get_date_time(qts, &date[0]); + cmos_get_date_time(qts, &date[1]); + cmos_get_date_time(qts, &date[2]); + cmos_get_date_time(qts, &date[3]); ts = time(NULL); gmtime_r(&ts, &end); @@ -207,7 +207,7 @@ static void bcd_check_time(void) g_assert_cmpint(ABS(t - s), <=, wiggle); } - qtest_quit(s); + qtest_quit(qts); } /* success if no crash or abort */ From 02e8828aa75376c8bb3e694d89e31b3ac8dd29df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 18 Sep 2023 08:25:49 +0200 Subject: [PATCH 48/80] tests/qtest/netdev-socket: Do not test multicast on Darwin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not run this test on Darwin, otherwise we get: qemu-system-arm: -netdev dgram,id=st0,remote.type=inet,remote.host=230.0.0.1,remote.port=1234: can't add socket to multicast group 230.0.0.1: Can't assign requested address Broken pipe ../../tests/qtest/libqtest.c:191: kill_qemu() tried to terminate QEMU process but encountered exit status 1 (expected 0) Abort trap: 6 Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20230918062549.2363-1-philmd@linaro.org> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/qtest/netdev-socket.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index b2501d72a1..7ba1eff120 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -401,7 +401,7 @@ static void test_dgram_inet(void) qtest_quit(qts0); } -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) static void test_dgram_mcast(void) { QTestState *qts; @@ -414,7 +414,9 @@ static void test_dgram_mcast(void) qtest_quit(qts); } +#endif +#ifndef _WIN32 static void test_dgram_unix(void) { QTestState *qts0, *qts1; @@ -511,7 +513,7 @@ int main(int argc, char **argv) if (has_ipv4) { qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4); qtest_add_func("/netdev/dgram/inet", test_dgram_inet); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast); #endif } From 4032f04c638ff852fa6f2d274f8dc2402b965ca9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 13 Sep 2023 18:09:21 +0200 Subject: [PATCH 49/80] hw/mips/jazz: Move the NIC init code into a separate function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mips_jazz_init() function is already quite big, so moving away some code here can help to make it more understandable. Additionally, by moving this code into a separate function, the next patch (that will refactor the for-loop around the NIC init code) will be much shorter and easier to understand. Message-ID: <20230913160922.355640-2-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/mips/jazz.c | 62 ++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 0081dcf921..829c9248e5 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -114,6 +114,40 @@ static const MemoryRegionOps dma_dummy_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void mips_jazz_init_net(NICInfo *nd, IOMMUMemoryRegion *rc4030_dma_mr, + DeviceState *rc4030, MemoryRegion *dp8393x_prom) +{ + DeviceState *dev; + SysBusDevice *sysbus; + int checksum, i; + uint8_t *prom; + + qemu_check_nic_model(nd, "dp83932"); + + dev = qdev_new("dp8393x"); + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint8(dev, "it_shift", 2); + qdev_prop_set_bit(dev, "big_endian", TARGET_BIG_ENDIAN); + object_property_set_link(OBJECT(dev), "dma_mr", + OBJECT(rc4030_dma_mr), &error_abort); + sysbus = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_mmio_map(sysbus, 0, 0x80001000); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4)); + + /* Add MAC address with valid checksum to PROM */ + prom = memory_region_get_ram_ptr(dp8393x_prom); + checksum = 0; + for (i = 0; i < 6; i++) { + prom[i] = nd->macaddr.a[i]; + checksum += prom[i]; + if (checksum > 0xff) { + checksum = (checksum + 1) & 0xff; + } + } + prom[7] = 0xff - checksum; +} + #define MAGNUM_BIOS_SIZE_MAX 0x7e000 #define MAGNUM_BIOS_SIZE \ (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) @@ -287,33 +321,7 @@ static void mips_jazz_init(MachineState *machine, nd->model = g_strdup("dp83932"); } if (strcmp(nd->model, "dp83932") == 0) { - int checksum, i; - uint8_t *prom; - - qemu_check_nic_model(nd, "dp83932"); - - dev = qdev_new("dp8393x"); - qdev_set_nic_properties(dev, nd); - qdev_prop_set_uint8(dev, "it_shift", 2); - qdev_prop_set_bit(dev, "big_endian", TARGET_BIG_ENDIAN); - object_property_set_link(OBJECT(dev), "dma_mr", - OBJECT(rc4030_dma_mr), &error_abort); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 0, 0x80001000); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4)); - - /* Add MAC address with valid checksum to PROM */ - prom = memory_region_get_ram_ptr(dp8393x_prom); - checksum = 0; - for (i = 0; i < 6; i++) { - prom[i] = nd->macaddr.a[i]; - checksum += prom[i]; - if (checksum > 0xff) { - checksum = (checksum + 1) & 0xff; - } - } - prom[7] = 0xff - checksum; + mips_jazz_init_net(nd, rc4030_dma_mr, rc4030, dp8393x_prom); break; } else if (is_help_option(nd->model)) { error_report("Supported NICs: dp83932"); From c9daa685cb69dddfe8082f09fd26728e98a66102 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 13 Sep 2023 18:09:22 +0200 Subject: [PATCH 50/80] hw/mips/jazz: Simplify the NIC setup code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The for-loop does not make much sense here - it is always left after the first iteration, so we can also check for nb_nics == 1 instead which is way easier to understand. Also, the checks for nd->model are superfluous since the code in mips_jazz_init_net() calls qemu_check_nic_model() that already takes care of this (i.e. initializing nd->model if it has not been set yet, and checking whether it is the "help" option or the supported NIC model). Message-ID: <20230913160922.355640-3-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth --- hw/mips/jazz.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 829c9248e5..c32d2b0b0a 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -172,7 +172,6 @@ static void mips_jazz_init(MachineState *machine, MemoryRegion *rtc = g_new(MemoryRegion, 1); MemoryRegion *dma_dummy = g_new(MemoryRegion, 1); MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1); - NICInfo *nd; DeviceState *dev, *rc4030; MMIOKBDState *i8042; SysBusDevice *sysbus; @@ -315,21 +314,11 @@ static void mips_jazz_init(MachineState *machine, } /* Network controller */ - for (n = 0; n < nb_nics; n++) { - nd = &nd_table[n]; - if (!nd->model) { - nd->model = g_strdup("dp83932"); - } - if (strcmp(nd->model, "dp83932") == 0) { - mips_jazz_init_net(nd, rc4030_dma_mr, rc4030, dp8393x_prom); - break; - } else if (is_help_option(nd->model)) { - error_report("Supported NICs: dp83932"); - exit(1); - } else { - error_report("Unsupported NIC: %s", nd->model); - exit(1); - } + if (nb_nics == 1) { + mips_jazz_init_net(&nd_table[0], rc4030_dma_mr, rc4030, dp8393x_prom); + } else if (nb_nics > 1) { + error_report("This machine only supports one NIC"); + exit(1); } /* SCSI adapter */ From b821109583a035a17fa5b89c0ebd8917d09cc82d Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Fri, 11 Aug 2023 10:06:08 +0300 Subject: [PATCH 51/80] tests/avocado: fix waiting for vm shutdown in replay_linux This patch fixes the race condition in waiting for shutdown of the replay linux test. Signed-off-by: Pavel Dovgalyuk Suggested-by: John Snow Message-ID: <20230811070608.3383343-4-pavel.dovgalyuk@ispras.ru> Signed-off-by: Thomas Huth --- tests/avocado/replay_linux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py index a76dd507fc..270ccc1eae 100644 --- a/tests/avocado/replay_linux.py +++ b/tests/avocado/replay_linux.py @@ -93,7 +93,7 @@ class ReplayLinux(LinuxTest): % os.path.getsize(replay_path)) else: vm.event_wait('SHUTDOWN', self.timeout) - vm.shutdown(True) + vm.wait() logger.info('successfully fihished the replay') elapsed = time.time() - start_time logger.info('elapsed time %.2f sec' % elapsed) From b2578459323c56002b360f5fc02188be7f5ca4db Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 29 Aug 2023 12:58:31 -0500 Subject: [PATCH 52/80] nbd: Prepare for 64-bit request effect lengths Widen the length field of NBDRequest to 64-bits, although we can assert that all current uses are still under 32 bits: either because of NBD_MAX_BUFFER_SIZE which is even smaller (and where size_t can still be appropriate, even on 32-bit platforms), or because nothing ever puts us into NBD_MODE_EXTENDED yet (and while future patches will allow larger transactions, the lengths in play here are still capped at 32-bit). There are no semantic changes, other than a typo fix in a couple of error messages. Signed-off-by: Eric Blake Message-ID: <20230829175826.377251-23-eblake@redhat.com> [eblake: fix assertion bug in nbd_co_send_simple_reply] Reviewed-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 25 +++++++++++++++++++------ block/trace-events | 2 +- include/block/nbd.h | 4 ++-- nbd/client.c | 1 + nbd/server.c | 23 +++++++++++++++-------- nbd/trace-events | 14 +++++++------- 6 files changed, 45 insertions(+), 24 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 9e99a4ddb5..4a7f37da1c 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1305,10 +1305,11 @@ nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, NBDRequest request = { .type = NBD_CMD_WRITE_ZEROES, .from = offset, - .len = bytes, /* .len is uint32_t actually */ + .len = bytes, }; - assert(bytes <= UINT32_MAX); /* rely on max_pwrite_zeroes */ + /* rely on max_pwrite_zeroes */ + assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); if (!(s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { @@ -1355,10 +1356,11 @@ nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) NBDRequest request = { .type = NBD_CMD_TRIM, .from = offset, - .len = bytes, /* len is uint32_t */ + .len = bytes, }; - assert(bytes <= UINT32_MAX); /* rely on max_pdiscard */ + /* rely on max_pdiscard */ + assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); if (!(s->info.flags & NBD_FLAG_SEND_TRIM) || !bytes) { @@ -1380,8 +1382,7 @@ static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( NBDRequest request = { .type = NBD_CMD_BLOCK_STATUS, .from = offset, - .len = MIN(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment), - MIN(bytes, s->info.size - offset)), + .len = MIN(bytes, s->info.size - offset), .flags = NBD_CMD_FLAG_REQ_ONE, }; @@ -1391,6 +1392,10 @@ static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( *file = bs; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; } + if (s->info.mode < NBD_MODE_EXTENDED) { + request.len = MIN(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment), + request.len); + } /* * Work around the fact that the block layer doesn't do @@ -1955,6 +1960,14 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.max_pwrite_zeroes = max; bs->bl.max_transfer = max; + /* + * Assume that if the server supports extended headers, it also + * supports unlimited size zero and trim commands. + */ + if (s->info.mode >= NBD_MODE_EXTENDED) { + bs->bl.max_pdiscard = bs->bl.max_pwrite_zeroes = 0; + } + if (s->info.opt_block && s->info.opt_block > bs->bl.opt_transfer) { bs->bl.opt_transfer = s->info.opt_block; diff --git a/block/trace-events b/block/trace-events index 6f121b7636..925aa554bb 100644 --- a/block/trace-events +++ b/block/trace-events @@ -167,7 +167,7 @@ iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, ui nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s" nbd_structured_read_compliance(const char *type) "server sent non-compliant unaligned read %s chunk" nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" -nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" +nbd_co_request_fail(uint64_t from, uint64_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu64 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" nbd_client_handshake(const char *export_name) "export '%s'" nbd_client_handshake_success(const char *export_name) "export '%s'" nbd_reconnect_attempt(unsigned in_flight) "in_flight %u" diff --git a/include/block/nbd.h b/include/block/nbd.h index 22a9b5d10e..8a765e78df 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -71,8 +71,8 @@ typedef enum NBDMode { */ typedef struct NBDRequest { uint64_t cookie; - uint64_t from; - uint32_t len; + uint64_t from; /* Offset touched by the command */ + uint64_t len; /* Effect length; 32 bit limit without extended headers */ uint16_t flags; /* NBD_CMD_FLAG_* */ uint16_t type; /* NBD_CMD_* */ NBDMode mode; /* Determines which network representation to use */ diff --git a/nbd/client.c b/nbd/client.c index 345f1c0f2d..cecb0f0437 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1349,6 +1349,7 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request) uint8_t buf[NBD_REQUEST_SIZE]; assert(request->mode <= NBD_MODE_STRUCTURED); /* TODO handle extended */ + assert(request->len <= UINT32_MAX); trace_nbd_send_request(request->from, request->len, request->cookie, request->flags, request->type, nbd_cmd_lookup(request->type)); diff --git a/nbd/server.c b/nbd/server.c index 35fcd9fdbc..0ca0a4c5c2 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1165,7 +1165,7 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) client->optlen = length; if (length > NBD_MAX_BUFFER_SIZE) { - error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", + error_setg(errp, "len (%" PRIu32 ") is larger than max len (%u)", length, NBD_MAX_BUFFER_SIZE); return -EINVAL; } @@ -1437,7 +1437,7 @@ static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest *reque request->type = lduw_be_p(buf + 6); request->cookie = ldq_be_p(buf + 8); request->from = ldq_be_p(buf + 16); - request->len = ldl_be_p(buf + 24); + request->len = (uint32_t)ldl_be_p(buf + 24); /* widen 32 to 64 bits */ trace_nbd_receive_request(magic, request->flags, request->type, request->from, request->len); @@ -1887,7 +1887,7 @@ static int coroutine_fn nbd_co_send_simple_reply(NBDClient *client, NBDRequest *request, uint32_t error, void *data, - size_t len, + uint64_t len, Error **errp) { NBDSimpleReply reply; @@ -1898,6 +1898,7 @@ static int coroutine_fn nbd_co_send_simple_reply(NBDClient *client, }; assert(!len || !nbd_err); + assert(len <= NBD_MAX_BUFFER_SIZE); assert(client->mode < NBD_MODE_STRUCTURED || (client->mode == NBD_MODE_STRUCTURED && request->type != NBD_CMD_READ)); @@ -1956,7 +1957,7 @@ static int coroutine_fn nbd_co_send_chunk_read(NBDClient *client, NBDRequest *request, uint64_t offset, void *data, - size_t size, + uint64_t size, bool final, Error **errp) { @@ -1968,7 +1969,7 @@ static int coroutine_fn nbd_co_send_chunk_read(NBDClient *client, {.iov_base = data, .iov_len = size} }; - assert(size); + assert(size && size <= NBD_MAX_BUFFER_SIZE); trace_nbd_co_send_chunk_read(request->cookie, offset, data, size); set_be_chunk(client, iov, 3, final ? NBD_REPLY_FLAG_DONE : 0, NBD_REPLY_TYPE_OFFSET_DATA, request); @@ -2011,13 +2012,14 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, NBDRequest *request, uint64_t offset, uint8_t *data, - size_t size, + uint64_t size, Error **errp) { int ret = 0; NBDExport *exp = client->exp; size_t progress = 0; + assert(size <= NBD_MAX_BUFFER_SIZE); while (progress < size) { int64_t pnum; int status = blk_co_block_status_above(exp->common.blk, NULL, @@ -2347,7 +2349,7 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * request->type == NBD_CMD_CACHE) { if (request->len > NBD_MAX_BUFFER_SIZE) { - error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", + error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)", request->len, NBD_MAX_BUFFER_SIZE); return -EINVAL; } @@ -2363,6 +2365,7 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * } if (request->type == NBD_CMD_WRITE) { + assert(request->len <= NBD_MAX_BUFFER_SIZE); if (nbd_read(client->ioc, req->data, request->len, "CMD_WRITE data", errp) < 0) { @@ -2384,7 +2387,7 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * } if (request->from > client->exp->size || request->len > client->exp->size - request->from) { - error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32 + error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu64 ", Size: %" PRIu64, request->from, request->len, client->exp->size); return (request->type == NBD_CMD_WRITE || @@ -2446,6 +2449,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, NBDExport *exp = client->exp; assert(request->type == NBD_CMD_READ); + assert(request->len <= NBD_MAX_BUFFER_SIZE); /* XXX: NBD Protocol only documents use of FUA with WRITE */ if (request->flags & NBD_CMD_FLAG_FUA) { @@ -2496,6 +2500,7 @@ static coroutine_fn int nbd_do_cmd_cache(NBDClient *client, NBDRequest *request, NBDExport *exp = client->exp; assert(request->type == NBD_CMD_CACHE); + assert(request->len <= NBD_MAX_BUFFER_SIZE); ret = blk_co_preadv(exp->common.blk, request->from, request->len, NULL, BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH); @@ -2529,6 +2534,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, if (request->flags & NBD_CMD_FLAG_FUA) { flags |= BDRV_REQ_FUA; } + assert(request->len <= NBD_MAX_BUFFER_SIZE); ret = blk_co_pwrite(exp->common.blk, request->from, request->len, data, flags); return nbd_send_generic_reply(client, request, ret, @@ -2572,6 +2578,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, return nbd_send_generic_reply(client, request, -EINVAL, "need non-zero length", errp); } + assert(request->len <= UINT32_MAX); if (client->export_meta.count) { bool dont_fragment = request->flags & NBD_CMD_FLAG_REQ_ONE; int contexts_remaining = client->export_meta.count; diff --git a/nbd/trace-events b/nbd/trace-events index f19a4d0db3..f9dccfcfb4 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -31,7 +31,7 @@ nbd_client_loop(void) "Doing NBD loop" nbd_client_loop_ret(int ret, const char *error) "NBD loop returned %d: %s" nbd_client_clear_queue(void) "Clearing NBD queue" nbd_client_clear_socket(void) "Clearing NBD socket" -nbd_send_request(uint64_t from, uint32_t len, uint64_t cookie, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .cookie = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) }" +nbd_send_request(uint64_t from, uint64_t len, uint64_t cookie, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu64 ", .cookie = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) }" nbd_receive_simple_reply(int32_t error, const char *errname, uint64_t cookie) "Got simple reply: { .error = %" PRId32 " (%s), cookie = %" PRIu64" }" nbd_receive_structured_reply_chunk(uint16_t flags, uint16_t type, const char *name, uint64_t cookie, uint32_t length) "Got structured reply chunk: { flags = 0x%" PRIx16 ", type = %d (%s), cookie = %" PRIu64 ", length = %" PRIu32 " }" @@ -60,18 +60,18 @@ nbd_negotiate_options_check_option(uint32_t option, const char *name) "Checking nbd_negotiate_begin(void) "Beginning negotiation" nbd_negotiate_new_style_size_flags(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags 0x%x" nbd_negotiate_success(void) "Negotiation succeeded" -nbd_receive_request(uint32_t magic, uint16_t flags, uint16_t type, uint64_t from, uint32_t len) "Got request: { magic = 0x%" PRIx32 ", .flags = 0x%" PRIx16 ", .type = 0x%" PRIx16 ", from = %" PRIu64 ", len = %" PRIu32 " }" +nbd_receive_request(uint32_t magic, uint16_t flags, uint16_t type, uint64_t from, uint64_t len) "Got request: { magic = 0x%" PRIx32 ", .flags = 0x%" PRIx16 ", .type = 0x%" PRIx16 ", from = %" PRIu64 ", len = %" PRIu64 " }" nbd_blk_aio_attached(const char *name, void *ctx) "Export %s: Attaching clients to AIO context %p" nbd_blk_aio_detach(const char *name, void *ctx) "Export %s: Detaching clients from AIO context %p" -nbd_co_send_simple_reply(uint64_t cookie, uint32_t error, const char *errname, int len) "Send simple reply: cookie = %" PRIu64 ", error = %" PRIu32 " (%s), len = %d" +nbd_co_send_simple_reply(uint64_t cookie, uint32_t error, const char *errname, uint64_t len) "Send simple reply: cookie = %" PRIu64 ", error = %" PRIu32 " (%s), len = %" PRIu64 nbd_co_send_chunk_done(uint64_t cookie) "Send structured reply done: cookie = %" PRIu64 -nbd_co_send_chunk_read(uint64_t cookie, uint64_t offset, void *data, size_t size) "Send structured read data reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu" -nbd_co_send_chunk_read_hole(uint64_t cookie, uint64_t offset, size_t size) "Send structured read hole reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", len = %zu" +nbd_co_send_chunk_read(uint64_t cookie, uint64_t offset, void *data, uint64_t size) "Send structured read data reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %" PRIu64 +nbd_co_send_chunk_read_hole(uint64_t cookie, uint64_t offset, uint64_t size) "Send structured read hole reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", len = %" PRIu64 nbd_co_send_extents(uint64_t cookie, unsigned int extents, uint32_t id, uint64_t length, int last) "Send block status reply: cookie = %" PRIu64 ", extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)" nbd_co_send_chunk_error(uint64_t cookie, int err, const char *errname, const char *msg) "Send structured error reply: cookie = %" PRIu64 ", error = %d (%s), msg = '%s'" nbd_co_receive_request_decode_type(uint64_t cookie, uint16_t type, const char *name) "Decoding type: cookie = %" PRIu64 ", type = %" PRIu16 " (%s)" -nbd_co_receive_request_payload_received(uint64_t cookie, uint32_t len) "Payload received: cookie = %" PRIu64 ", len = %" PRIu32 -nbd_co_receive_align_compliance(const char *op, uint64_t from, uint32_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx32 ", align=0x%" PRIx32 +nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) "Payload received: cookie = %" PRIu64 ", len = %" PRIu64 +nbd_co_receive_align_compliance(const char *op, uint64_t from, uint64_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx64 ", align=0x%" PRIx32 nbd_trip(void) "Reading request" # client-connection.c From 8db7e2d65733268f7e788a69f5c6125f14f14cb0 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 29 Aug 2023 12:58:32 -0500 Subject: [PATCH 53/80] nbd/server: Refactor handling of command sanity checks Upcoming additions to support NBD 64-bit effect lengths will add a new command flag NBD_CMD_FLAG_PAYLOAD_LEN that needs to be considered in our sanity checks of the client's messages (that is, more than just CMD_WRITE have the potential to carry a client payload when extended headers are in effect). But before we can start to support that, it is easier to first refactor the existing set of various if statements over open-coded combinations of request->type to instead be a single switch statement over all command types that sets witnesses, then straight-line processing based on the witnesses. No semantic change is intended. Signed-off-by: Eric Blake Message-ID: <20230829175826.377251-24-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- nbd/server.c | 120 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/nbd/server.c b/nbd/server.c index 0ca0a4c5c2..7a6f95071f 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -2317,11 +2317,16 @@ static int coroutine_fn nbd_co_send_bitmap(NBDClient *client, * to the client (although the caller may still need to disconnect after * reporting the error). */ -static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, +static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, + NBDRequest *request, Error **errp) { NBDClient *client = req->client; - int valid_flags; + bool check_length = false; + bool check_rofs = false; + bool allocate_buffer = false; + unsigned payload_len = 0; + int valid_flags = NBD_CMD_FLAG_FUA; int ret; g_assert(qemu_in_coroutine()); @@ -2333,55 +2338,88 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * trace_nbd_co_receive_request_decode_type(request->cookie, request->type, nbd_cmd_lookup(request->type)); - - if (request->type != NBD_CMD_WRITE) { - /* No payload, we are ready to read the next request. */ - req->complete = true; - } - - if (request->type == NBD_CMD_DISC) { + switch (request->type) { + case NBD_CMD_DISC: /* Special case: we're going to disconnect without a reply, * whether or not flags, from, or len are bogus */ + req->complete = true; return -EIO; + + case NBD_CMD_READ: + if (client->mode >= NBD_MODE_STRUCTURED) { + valid_flags |= NBD_CMD_FLAG_DF; + } + check_length = true; + allocate_buffer = true; + break; + + case NBD_CMD_WRITE: + payload_len = request->len; + check_length = true; + allocate_buffer = true; + check_rofs = true; + break; + + case NBD_CMD_FLUSH: + break; + + case NBD_CMD_TRIM: + check_rofs = true; + break; + + case NBD_CMD_CACHE: + check_length = true; + break; + + case NBD_CMD_WRITE_ZEROES: + valid_flags |= NBD_CMD_FLAG_NO_HOLE | NBD_CMD_FLAG_FAST_ZERO; + check_rofs = true; + break; + + case NBD_CMD_BLOCK_STATUS: + valid_flags |= NBD_CMD_FLAG_REQ_ONE; + break; + + default: + /* Unrecognized, will fail later */ + ; } - if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE || - request->type == NBD_CMD_CACHE) - { - if (request->len > NBD_MAX_BUFFER_SIZE) { - error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)", - request->len, NBD_MAX_BUFFER_SIZE); - return -EINVAL; - } - - if (request->type != NBD_CMD_CACHE) { - req->data = blk_try_blockalign(client->exp->common.blk, - request->len); - if (req->data == NULL) { - error_setg(errp, "No memory"); - return -ENOMEM; - } + /* Payload and buffer handling. */ + if (!payload_len) { + req->complete = true; + } + if (check_length && request->len > NBD_MAX_BUFFER_SIZE) { + /* READ, WRITE, CACHE */ + error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)", + request->len, NBD_MAX_BUFFER_SIZE); + return -EINVAL; + } + if (allocate_buffer) { + /* READ, WRITE */ + req->data = blk_try_blockalign(client->exp->common.blk, + request->len); + if (req->data == NULL) { + error_setg(errp, "No memory"); + return -ENOMEM; } } - - if (request->type == NBD_CMD_WRITE) { - assert(request->len <= NBD_MAX_BUFFER_SIZE); - if (nbd_read(client->ioc, req->data, request->len, "CMD_WRITE data", - errp) < 0) - { + if (payload_len) { + /* WRITE */ + assert(req->data); + ret = nbd_read(client->ioc, req->data, payload_len, + "CMD_WRITE data", errp); + if (ret < 0) { return -EIO; } req->complete = true; - trace_nbd_co_receive_request_payload_received(request->cookie, - request->len); + payload_len); } /* Sanity checks. */ - if (client->exp->nbdflags & NBD_FLAG_READ_ONLY && - (request->type == NBD_CMD_WRITE || - request->type == NBD_CMD_WRITE_ZEROES || - request->type == NBD_CMD_TRIM)) { + if (client->exp->nbdflags & NBD_FLAG_READ_ONLY && check_rofs) { + /* WRITE, TRIM, WRITE_ZEROES */ error_setg(errp, "Export is read-only"); return -EROFS; } @@ -2404,14 +2442,6 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * request->len, client->check_align); } - valid_flags = NBD_CMD_FLAG_FUA; - if (request->type == NBD_CMD_READ && client->mode >= NBD_MODE_STRUCTURED) { - valid_flags |= NBD_CMD_FLAG_DF; - } else if (request->type == NBD_CMD_WRITE_ZEROES) { - valid_flags |= NBD_CMD_FLAG_NO_HOLE | NBD_CMD_FLAG_FAST_ZERO; - } else if (request->type == NBD_CMD_BLOCK_STATUS) { - valid_flags |= NBD_CMD_FLAG_REQ_ONE; - } if (request->flags & ~valid_flags) { error_setg(errp, "unsupported flags for command %s (got 0x%x)", nbd_cmd_lookup(request->type), request->flags); From ea985d235b868047cb4d8cb5657bcd8ad98c6ba2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Sep 2023 13:57:11 +0200 Subject: [PATCH 54/80] pc_piix: remove pc-i440fx-1.4 up to pc-i440fx-1.7 These are the last users of the 128K SeaBIOS blob in the i440FX family. Removing them allows us to drop PCI support from the 128K blob, thus making it easier to update SeaBIOS to newer versions. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- docs/about/deprecated.rst | 8 ---- docs/about/removed-features.rst | 4 +- hw/i386/pc.c | 60 ++---------------------- hw/i386/pc_piix.c | 73 ----------------------------- tests/qtest/test-x86-cpuid-compat.c | 10 +--- 5 files changed, 6 insertions(+), 149 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index dc4da95329..8f3fef97bd 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -277,14 +277,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-1.4`` up to ``pc-i440fx-1.7`` (since 7.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''' - -These old machine types are quite neglected nowadays and thus might have -various pitfalls with regards to live migration. Use a newer machine type -instead. - - Backend options --------------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index c2043fd415..97ec47f1d2 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -715,8 +715,8 @@ mips ``fulong2e`` machine alias (removed in 6.0) This machine has been renamed ``fuloong2e``. -``pc-0.10`` up to ``pc-1.3`` (removed in 4.0 up to 6.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``pc-0.10`` up to ``pc-i440fx-1.7`` (removed in 4.0 up to 8.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These machine types were very old and likely could not be used for live migration from old QEMU versions anymore. Use a newer machine type instead. diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 3db0743f31..5d399b6247 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -359,60 +359,6 @@ GlobalProperty pc_compat_2_0[] = { }; const size_t pc_compat_2_0_len = G_N_ELEMENTS(pc_compat_2_0); -GlobalProperty pc_compat_1_7[] = { - PC_CPU_MODEL_IDS("1.7.0") - { TYPE_USB_DEVICE, "msos-desc", "no" }, - { "PIIX4_PM", ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, "off" }, - { "hpet", HPET_INTCAP, "4" }, -}; -const size_t pc_compat_1_7_len = G_N_ELEMENTS(pc_compat_1_7); - -GlobalProperty pc_compat_1_6[] = { - PC_CPU_MODEL_IDS("1.6.0") - { "e1000", "mitigation", "off" }, - { "qemu64-" TYPE_X86_CPU, "model", "2" }, - { "qemu32-" TYPE_X86_CPU, "model", "3" }, - { "i440FX-pcihost", "short_root_bus", "1" }, - { "q35-pcihost", "short_root_bus", "1" }, -}; -const size_t pc_compat_1_6_len = G_N_ELEMENTS(pc_compat_1_6); - -GlobalProperty pc_compat_1_5[] = { - PC_CPU_MODEL_IDS("1.5.0") - { "Conroe-" TYPE_X86_CPU, "model", "2" }, - { "Conroe-" TYPE_X86_CPU, "min-level", "2" }, - { "Penryn-" TYPE_X86_CPU, "model", "2" }, - { "Penryn-" TYPE_X86_CPU, "min-level", "2" }, - { "Nehalem-" TYPE_X86_CPU, "model", "2" }, - { "Nehalem-" TYPE_X86_CPU, "min-level", "2" }, - { "virtio-net-pci", "any_layout", "off" }, - { TYPE_X86_CPU, "pmu", "on" }, - { "i440FX-pcihost", "short_root_bus", "0" }, - { "q35-pcihost", "short_root_bus", "0" }, -}; -const size_t pc_compat_1_5_len = G_N_ELEMENTS(pc_compat_1_5); - -GlobalProperty pc_compat_1_4[] = { - PC_CPU_MODEL_IDS("1.4.0") - { "scsi-hd", "discard_granularity", "0" }, - { "scsi-cd", "discard_granularity", "0" }, - { "ide-hd", "discard_granularity", "0" }, - { "ide-cd", "discard_granularity", "0" }, - { "virtio-blk-pci", "discard_granularity", "0" }, - /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string: */ - { "virtio-serial-pci", "vectors", "0xFFFFFFFF" }, - { "virtio-net-pci", "ctrl_guest_offloads", "off" }, - { "e1000", "romfile", "pxe-e1000.rom" }, - { "ne2k_pci", "romfile", "pxe-ne2k_pci.rom" }, - { "pcnet", "romfile", "pxe-pcnet.rom" }, - { "rtl8139", "romfile", "pxe-rtl8139.rom" }, - { "virtio-net-pci", "romfile", "pxe-virtio.rom" }, - { "486-" TYPE_X86_CPU, "model", "0" }, - { "n270" "-" TYPE_X86_CPU, "movbe", "off" }, - { "Westmere" "-" TYPE_X86_CPU, "pclmulqdq", "off" }, -}; -const size_t pc_compat_1_4_len = G_N_ELEMENTS(pc_compat_1_4); - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; @@ -1293,9 +1239,9 @@ void pc_basic_device_init(struct PCMachineState *pcms, exit(1); } /* - * For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7 and - * earlier, use IRQ2 for compat. Otherwise, use IRQ16~23, IRQ8 and - * IRQ2. + * For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-*, + * use IRQ16~23, IRQ8 and IRQ2. If the user has already set + * the property, use whatever mask they specified. */ uint8_t compat = object_property_get_uint(OBJECT(hpet), HPET_INTCAP, NULL); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8321f36f97..ff8654ecda 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -423,27 +423,6 @@ static void pc_compat_2_0_fn(MachineState *machine) pc_compat_2_1_fn(machine); } -static void pc_compat_1_7_fn(MachineState *machine) -{ - pc_compat_2_0_fn(machine); - x86_cpu_change_kvm_default("x2apic", NULL); -} - -static void pc_compat_1_6_fn(MachineState *machine) -{ - pc_compat_1_7_fn(machine); -} - -static void pc_compat_1_5_fn(MachineState *machine) -{ - pc_compat_1_6_fn(machine); -} - -static void pc_compat_1_4_fn(MachineState *machine) -{ - pc_compat_1_5_fn(machine); -} - #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { @@ -876,58 +855,6 @@ static void pc_i440fx_2_0_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0_fn, pc_i440fx_2_0_machine_options); -static void pc_i440fx_1_7_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_0_machine_options(m); - m->hw_version = "1.7.0"; - m->default_machine_opts = NULL; - m->option_rom_has_mr = true; - m->deprecation_reason = "old and unattended - use a newer version instead"; - compat_props_add(m->compat_props, pc_compat_1_7, pc_compat_1_7_len); - pcmc->smbios_defaults = false; - pcmc->gigabyte_align = false; - pcmc->legacy_acpi_table_size = 6414; -} - -DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7_fn, - pc_i440fx_1_7_machine_options); - -static void pc_i440fx_1_6_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_1_7_machine_options(m); - m->hw_version = "1.6.0"; - m->rom_file_has_mr = false; - compat_props_add(m->compat_props, pc_compat_1_6, pc_compat_1_6_len); - pcmc->has_acpi_build = false; -} - -DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6_fn, - pc_i440fx_1_6_machine_options); - -static void pc_i440fx_1_5_machine_options(MachineClass *m) -{ - pc_i440fx_1_6_machine_options(m); - m->hw_version = "1.5.0"; - compat_props_add(m->compat_props, pc_compat_1_5, pc_compat_1_5_len); -} - -DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5_fn, - pc_i440fx_1_5_machine_options); - -static void pc_i440fx_1_4_machine_options(MachineClass *m) -{ - pc_i440fx_1_5_machine_options(m); - m->hw_version = "1.4.0"; - compat_props_add(m->compat_props, pc_compat_1_4, pc_compat_1_4_len); -} - -DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4_fn, - pc_i440fx_1_4_machine_options); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index b39c9055b3..6a39454fce 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -313,18 +313,10 @@ int main(int argc, char **argv) "xlevel2", 0); } /* - * QEMU 1.4.0 had auto-level enabled for CPUID[7], already, + * QEMU 2.3.0 had auto-level enabled for CPUID[7], already, * and the compat code that sets default level shouldn't * disable the auto-level=7 code: */ - if (qtest_has_machine("pc-i440fx-1.4")) { - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.4/off", - "-machine pc-i440fx-1.4 -cpu Nehalem", - "level", 2); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.5/on", - "-machine pc-i440fx-1.4 -cpu Nehalem,smap=on", - "level", 7); - } if (qtest_has_machine("pc-i440fx-2.3")) { add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", "-machine pc-i440fx-2.3 -cpu Penryn", From a1fadbcf482c38407a8ff488b0f9a3c4332802d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 15 Sep 2023 14:10:09 +0200 Subject: [PATCH 55/80] seabios: remove PCI drivers from bios.bin bios.bin is now used only by ISA PC, so PCI drivers are not necessary. Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini --- pc-bios/bios.bin | Bin 131072 -> 131072 bytes roms/config.seabios-128k | 29 +++++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 6a196cf72a01fddb3256d4ae2183dbb701196993..d3abd947dad25bde05e8b8305d94ab2f62e75cf6 100644 GIT binary patch literal 131072 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

aFgpgS0+UMh%41b*;t`I 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\BpQ}Gn|B@NGrFEEf+!iaUm2C129`u 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 diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index d18c802c46..0b144bb1de 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -1,21 +1,30 @@ -# for qemu machine types 1.7 + older -# need to turn off features (xhci,uas) to make it fit into 128k +# SeaBIOS Configuration for -M isapc + CONFIG_QEMU=y CONFIG_ROM_SIZE=128 CONFIG_ATA_DMA=n -CONFIG_BOOTSPLASH=n CONFIG_XEN=n -CONFIG_USB_OHCI=n -CONFIG_USB_XHCI=n -CONFIG_USB_UAS=n +CONFIG_ATA_PIO32=n +CONFIG_AHCI=n CONFIG_SDCARD=n -CONFIG_TCGBIOS=n -CONFIG_MPT_SCSI=n -CONFIG_ESP_SCSI=n -CONFIG_MEGASAS=n +CONFIG_VIRTIO_BLK=n +CONFIG_VIRTIO_SCSI=n CONFIG_PVSCSI=n +CONFIG_ESP_SCSI=n +CONFIG_LSI_SCSI=n +CONFIG_MEGASAS=n +CONFIG_MPT_SCSI=n CONFIG_NVME=n CONFIG_USE_SMM=n CONFIG_VGAHOOKS=n CONFIG_HOST_BIOS_GEOMETRY=n +CONFIG_USB=n +CONFIG_PMTIMER=n +CONFIG_PCIBIOS=n +CONFIG_DISABLE_A20=n +CONFIG_WRITABLE_UPPERMEMORY=n +CONFIG_TCGBIOS=n +CONFIG_ACPI=n CONFIG_ACPI_PARSE=n +CONFIG_DEBUG_SERIAL=n +CONFIG_DEBUG_SERIAL_MMIO=n From 8a9fc82bac139f8814ed6ae338381a3992eb414c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 11:06:09 +0200 Subject: [PATCH 56/80] user-exec-stub: remove unused variable enable_cpu_pm is only used by softmmu-specific code, namely target/i386/host-cpu.c and target/i386/kvm/*. It does not need a stub definition anymore. Signed-off-by: Paolo Bonzini --- accel/tcg/user-exec-stub.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index 874e1f1a20..2dc6fd9c4e 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -2,8 +2,6 @@ #include "hw/core/cpu.h" #include "exec/replay-core.h" -bool enable_cpu_pm = false; - void cpu_resume(CPUState *cpu) { } From 7cfcc79b0ab800959716738aff9419f53fc68c9c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 25 Sep 2023 11:18:54 +0200 Subject: [PATCH 57/80] hw/scsi/scsi-disk: Disallow block sizes smaller than 512 [CVE-2023-42467] We are doing things like nb_sectors /= (s->qdev.blocksize / BDRV_SECTOR_SIZE); in the code here (e.g. in scsi_disk_emulate_mode_sense()), so if the blocksize is smaller than BDRV_SECTOR_SIZE (=512), this crashes with a division by 0 exception. Thus disallow block sizes of 256 bytes to avoid this situation. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1813 CVE: 2023-42467 Signed-off-by: Thomas Huth Message-ID: <20230925091854.49198-1-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e0d79c7966..477ee2bcd4 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1628,9 +1628,10 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) * Since the existing code only checks/updates bits 8-15 of the block * size, restrict ourselves to the same requirement for now to ensure * that a block size set by a block descriptor and then read back by - * a subsequent SCSI command will be the same + * a subsequent SCSI command will be the same. Also disallow a block + * size of 256 since we cannot handle anything below BDRV_SECTOR_SIZE. */ - if (bs && !(bs & ~0xff00) && bs != s->qdev.blocksize) { + if (bs && !(bs & ~0xfe00) && bs != s->qdev.blocksize) { s->qdev.blocksize = bs; trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize); } From c431ffd47157ad4bd3a230570a31faa088c71260 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:22:50 +0200 Subject: [PATCH 58/80] vl: remove shadowed local variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- softmmu/vl.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/softmmu/vl.c b/softmmu/vl.c index db04f98c36..59a472a0b1 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -3214,7 +3214,6 @@ void qemu_init(int argc, char **argv) } break; case QEMU_OPTION_watchdog_action: { - QemuOpts *opts; opts = qemu_opts_create(qemu_find_opts("action"), NULL, 0, &error_abort); qemu_opt_set(opts, "watchdog", optarg, &error_abort); break; @@ -3525,16 +3524,16 @@ void qemu_init(int argc, char **argv) break; case QEMU_OPTION_compat: { - CompatPolicy *opts; + CompatPolicy *opts_policy; Visitor *v; v = qobject_input_visitor_new_str(optarg, NULL, &error_fatal); - visit_type_CompatPolicy(v, NULL, &opts, &error_fatal); - QAPI_CLONE_MEMBERS(CompatPolicy, &compat_policy, opts); + visit_type_CompatPolicy(v, NULL, &opts_policy, &error_fatal); + QAPI_CLONE_MEMBERS(CompatPolicy, &compat_policy, opts_policy); - qapi_free_CompatPolicy(opts); + qapi_free_CompatPolicy(opts_policy); visit_free(v); break; } From 0cb9c5880e6b8dedc4e20026ce859dd1ea9aac84 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 13:06:46 +0200 Subject: [PATCH 59/80] ui/vnc: fix debug output for invalid audio message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The debug message was cut and pasted from the invalid audio format case, but the audio message is at bytes 2-3. Reviewed-by: Daniel P. Berrangé Signed-off-by: Paolo Bonzini --- ui/vnc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/vnc.c b/ui/vnc.c index 6fd86996a5..1684ab9096 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2551,7 +2551,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) vs, vs->ioc, vs->as.fmt, vs->as.nchannels, vs->as.freq); break; default: - VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4)); + VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 2)); vnc_client_error(vs); break; } From 477b301000d665313217f65e3a368d2cb7769c42 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 13:05:58 +0200 Subject: [PATCH 60/80] ui/vnc: fix handling of VNC_FEATURE_XVP VNC_FEATURE_XVP was not shifted left before adding it to vs->features, so it was never enabled; but it was also checked the wrong way with a logical AND instead of vnc_has_feature. Fix both places. Signed-off-by: Paolo Bonzini --- ui/vnc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 1684ab9096..c302bb07a5 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2205,7 +2205,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) break; case VNC_ENCODING_XVP: if (vs->vd->power_control) { - vs->features |= VNC_FEATURE_XVP; + vs->features |= VNC_FEATURE_XVP_MASK; send_xvp_message(vs, VNC_XVP_CODE_INIT); } break; @@ -2454,7 +2454,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) vnc_client_cut_text(vs, read_u32(data, 4), data + 8); break; case VNC_MSG_CLIENT_XVP: - if (!(vs->features & VNC_FEATURE_XVP)) { + if (!vnc_has_feature(vs, VNC_FEATURE_XVP)) { error_report("vnc: xvp client message while disabled"); vnc_client_error(vs); break; From 4c186847ee0080a76dfef874322d634c429d43ad Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:05:37 +0200 Subject: [PATCH 61/80] mptsas: avoid shadowed local variables Rename the argument so that "addr" is only used inside the for loop. Signed-off-by: Paolo Bonzini --- hw/scsi/mptsas.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 3de288b454..75d3ab8bd1 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -192,7 +192,7 @@ static dma_addr_t mptsas_ld_sg_base(MPTSASState *s, uint32_t flags_and_length, return addr; } -static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr) +static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr req_addr) { PCIDevice *pci = (PCIDevice *) s; hwaddr next_chain_addr; @@ -201,8 +201,8 @@ static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr) uint32_t chain_offset; chain_offset = req->scsi_io.ChainOffset; - next_chain_addr = addr + chain_offset * sizeof(uint32_t); - sgaddr = addr + sizeof(MPIMsgSCSIIORequest); + next_chain_addr = req_addr + chain_offset * sizeof(uint32_t); + sgaddr = req_addr + sizeof(MPIMsgSCSIIORequest); pci_dma_sglist_init(&req->qsg, pci, 4); left = req->scsi_io.DataLength; From 973d3ea5a1c0573149b7004108276ca01cb05fd2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:21:29 +0200 Subject: [PATCH 62/80] pm_smbus: rename variable to avoid shadowing Acked-by: Corey Minyard Signed-off-by: Paolo Bonzini --- hw/i2c/pm_smbus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 9ad6a47739..4e1b8a5182 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -279,7 +279,7 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, if (!read && s->smb_index == s->smb_data0) { uint8_t prot = (s->smb_ctl >> 2) & 0x07; uint8_t cmd = s->smb_cmd; - uint8_t addr = s->smb_addr >> 1; + uint8_t smb_addr = s->smb_addr >> 1; int ret; if (prot == PROT_I2C_BLOCK_READ) { @@ -287,7 +287,7 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, goto out; } - ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data, + ret = smbus_write_block(s->smbus, smb_addr, cmd, s->smb_data, s->smb_data0, !s->i2c_enable); if (ret < 0) { s->smb_stat |= STS_DEV_ERR; From 168d46749d19f4808022b9a88c0846b3286aed59 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:27:24 +0200 Subject: [PATCH 63/80] m48t59-test: avoid possible overflow on ABS Originally meant to avoid a shadowed variable "s", which was fixed by renaming the outer declaration to "qts". Avoid the chance of an overflow in the computation of ABS(t - s). Signed-off-by: Paolo Bonzini --- tests/qtest/m48t59-test.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index 9487faff1a..b9cd209165 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -192,19 +192,22 @@ static void bcd_check_time(void) } if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) { - long t, s; + long date_s, start_s; + unsigned long diff; start.tm_isdst = datep->tm_isdst; - t = (long)mktime(datep); - s = (long)mktime(&start); - if (t < s) { - g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); + date_s = (long)mktime(datep); + start_s = (long)mktime(&start); + if (date_s < start_s) { + diff = start_s - date_s; + g_test_message("RTC is %ld second(s) behind wall-clock", diff); } else { - g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); + diff = date_s - start_s; + g_test_message("RTC is %ld second(s) ahead of wall-clock", diff); } - g_assert_cmpint(ABS(t - s), <=, wiggle); + g_assert_cmpint(diff, <=, wiggle); } qtest_quit(qts); From e0c3ef715baabbea6d59335bebfc7e40f1724d7e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:19:34 +0200 Subject: [PATCH 64/80] target/i386/kvm: eliminate shadowed local variables These are harmless are they die immediately after their use. Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index af101fcdf6..f6c7f7e268 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2699,8 +2699,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) if (enable_cpu_pm) { int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS); - int ret; - /* Work around for kernel header with a typo. TODO: fix header and drop. */ #if defined(KVM_X86_DISABLE_EXITS_HTL) && !defined(KVM_X86_DISABLE_EXITS_HLT) #define KVM_X86_DISABLE_EXITS_HLT KVM_X86_DISABLE_EXITS_HTL @@ -3610,7 +3608,7 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (kvm_enabled() && cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { uint64_t depth; - int i, ret; + int ret; /* * Only migrate Arch LBR states when the host Arch LBR depth @@ -3643,8 +3641,6 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } if (env->mcg_cap) { - int i; - kvm_msr_entry_add(cpu, MSR_MCG_STATUS, env->mcg_status); kvm_msr_entry_add(cpu, MSR_MCG_CTL, env->mcg_ctl); if (has_msr_mcg_ext_ctl) { @@ -4041,7 +4037,6 @@ static int kvm_get_msrs(X86CPU *cpu) if (kvm_enabled() && cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { uint64_t depth; - int i, ret; ret = kvm_get_one_msr(cpu, MSR_ARCH_LBR_DEPTH, &depth); if (ret == 1 && depth == ARCH_LBR_NR_ENTRIES) { From 637123a21381b2c5833d9d663af06e3105d8caa9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:06:02 +0200 Subject: [PATCH 65/80] target/i386/cpu: avoid shadowed local variables Reuse the pointer variable used for the unversioned model. Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 7836aa6692..ed72883bf3 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5976,9 +5976,10 @@ static void x86_register_cpudef_types(const X86CPUDefinition *def) /* Versioned models: */ for (vdef = x86_cpu_def_get_versions(def); vdef->version; vdef++) { - X86CPUModel *m = g_new0(X86CPUModel, 1); g_autofree char *name = x86_cpu_versioned_model_name(def, vdef->version); + + m = g_new0(X86CPUModel, 1); m->cpudef = def; m->version = vdef->version; m->note = vdef->note; From 19729affe1cd191f063db4b4d43058974cf43bc9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:07:21 +0200 Subject: [PATCH 66/80] target/i386/translate: avoid shadowed local variables Just remove the declaration. There is nothing in the function after the switch statement, so it is safe to do. Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index c98e42f17a..72635b87d3 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3242,7 +3242,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) case 0x30 ... 0x35: case 0x38 ... 0x3d: { - int op, f, val; + int f; op = (b >> 3) & 7; f = (b >> 1) & 3; @@ -3302,8 +3302,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) case 0x81: case 0x83: { - int val; - ot = mo_b_d(b, dflag); modrm = x86_ldub_code(env, s); From a908985971a38d335114e36f0b8b42fd85816cbe Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:16:00 +0200 Subject: [PATCH 67/80] target/i386/seg_helper: introduce tss_set_busy Eliminate a shadowed local variable in the process. Signed-off-by: Paolo Bonzini --- target/i386/tcg/seg_helper.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index e8d19c65fd..b5198db82b 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -226,6 +226,21 @@ static void tss_load_seg(CPUX86State *env, X86Seg seg_reg, int selector, } } +static void tss_set_busy(CPUX86State *env, int tss_selector, bool value, + uintptr_t retaddr) +{ + target_ulong ptr = env->gdt.base + (env->tr.selector & ~7); + uint32_t e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); + + if (value) { + e2 |= DESC_TSS_BUSY_MASK; + } else { + e2 &= ~DESC_TSS_BUSY_MASK; + } + + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); +} + #define SWITCH_TSS_JMP 0 #define SWITCH_TSS_IRET 1 #define SWITCH_TSS_CALL 2 @@ -341,13 +356,7 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, /* clear busy bit (it is restartable) */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { - target_ulong ptr; - uint32_t e2; - - ptr = env->gdt.base + (env->tr.selector & ~7); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - e2 &= ~DESC_TSS_BUSY_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); + tss_set_busy(env, env->tr.selector, 0, retaddr); } old_eflags = cpu_compute_eflags(env); if (source == SWITCH_TSS_IRET) { @@ -399,13 +408,7 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, /* set busy bit */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { - target_ulong ptr; - uint32_t e2; - - ptr = env->gdt.base + (tss_selector & ~7); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); + tss_set_busy(env, tss_selector, 1, retaddr); } /* set the new CPU state */ From 49958057a2ff7503357ce624da0cd3a94ab554bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:35:42 +0200 Subject: [PATCH 68/80] target/i386/seg_helper: remove shadowed variable Return the width of the new task directly from switch_tss_ra. Signed-off-by: Paolo Bonzini --- target/i386/tcg/seg_helper.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index b5198db82b..2b92aee207 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -245,10 +245,10 @@ static void tss_set_busy(CPUX86State *env, int tss_selector, bool value, #define SWITCH_TSS_IRET 1 #define SWITCH_TSS_CALL 2 -/* XXX: restore CPU state in registers (PowerPC case) */ -static void switch_tss_ra(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip, uintptr_t retaddr) +/* return 0 if switching to a 16-bit selector */ +static int switch_tss_ra(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip, uintptr_t retaddr) { int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; target_ulong tss_base; @@ -502,13 +502,14 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK); } #endif + return type >> 3; } -static void switch_tss(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip) +static int switch_tss(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip) { - switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); + return switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); } static inline unsigned int get_sp_mask(unsigned int e2) @@ -650,14 +651,11 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, if (!(e2 & DESC_P_MASK)) { raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); } - switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); + shift = switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); if (has_error_code) { - int type; uint32_t mask; /* push the error code */ - type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; - shift = type >> 3; if (env->segs[R_SS].flags & DESC_B_MASK) { mask = 0xffffffff; } else { From 1bce34aaa9d324b6d4aaf681e634e1840ca5d04e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 Sep 2023 12:20:06 +0200 Subject: [PATCH 69/80] target/i386/svm_helper: eliminate duplicate local variable This shadows an outer "cs" variable that is initialized to the same expression. Signed-off-by: Paolo Bonzini --- target/i386/tcg/sysemu/svm_helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 2d27731b60..32ff0dbb13 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -387,8 +387,6 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->hflags2 |= HF2_GIF_MASK; if (ctl_has_irq(env)) { - CPUState *cs = env_cpu(env); - cs->interrupt_request |= CPU_INTERRUPT_VIRQ; } From e2dbca033710efea20b0b0a26ca05570dcdabd49 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Apr 2023 11:23:00 +0200 Subject: [PATCH 70/80] block: mark mixed functions that can suspend The marking should be extended transitively to all functions that call these ones, so that static analysis can be done much more efficiently. However, this is a start and makes it possible to use vrc's path-based searches to find potential bugs where coroutine_fns call blocking functions. Signed-off-by: Paolo Bonzini --- block/io.c | 5 +++-- block/qcow2.c | 2 +- block/qed.c | 4 ++-- block/throttle-groups.c | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/block/io.c b/block/io.c index 209a6da0c8..e7f9448d5a 100644 --- a/block/io.c +++ b/block/io.c @@ -387,7 +387,8 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent) bdrv_do_drained_begin(bs, parent, false); } -void bdrv_drained_begin(BlockDriverState *bs) +void coroutine_mixed_fn +bdrv_drained_begin(BlockDriverState *bs) { IO_OR_GS_CODE(); bdrv_do_drained_begin(bs, NULL, true); @@ -506,7 +507,7 @@ void bdrv_drain_all_begin_nopoll(void) } } -void bdrv_drain_all_begin(void) +void coroutine_mixed_fn bdrv_drain_all_begin(void) { BlockDriverState *bs = NULL; diff --git a/block/qcow2.c b/block/qcow2.c index af43d59d76..5a3c537f14 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5288,7 +5288,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, return spec_info; } -static int qcow2_has_zero_init(BlockDriverState *bs) +static int coroutine_mixed_fn qcow2_has_zero_init(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; bool preallocated; diff --git a/block/qed.c b/block/qed.c index b2604d9dad..45ae320290 100644 --- a/block/qed.c +++ b/block/qed.c @@ -570,8 +570,8 @@ static void coroutine_fn bdrv_qed_open_entry(void *opaque) qemu_co_mutex_unlock(&s->table_lock); } -static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int coroutine_mixed_fn bdrv_qed_open(BlockDriverState *bs, QDict *options, + int flags, Error **errp) { QEDOpenCo qoc = { .bs = bs, diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 3eda4c4e3d..f5c0fac581 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -317,8 +317,8 @@ static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tg * @tgm: the current ThrottleGroupMember * @direction: the ThrottleDirection */ -static void schedule_next_request(ThrottleGroupMember *tgm, - ThrottleDirection direction) +static void coroutine_mixed_fn schedule_next_request(ThrottleGroupMember *tgm, + ThrottleDirection direction) { ThrottleState *ts = tgm->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); From d79b9202e45711e37e5ba5b3fbfccb4b9fff78a1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 16 Dec 2022 12:53:44 +0100 Subject: [PATCH 71/80] compiler: introduce QEMU_ANNOTATE Allow a more shorter syntax when defining wrapper macros for __attribute__((annotate(...))). Signed-off-by: Paolo Bonzini --- include/qemu/compiler.h | 6 ++++++ include/qemu/osdep.h | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index a309f90c76..7fda29b445 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -197,4 +197,10 @@ #define BUILTIN_SUBCLL_BROKEN #endif +#if __has_attribute(annotate) +#define QEMU_ANNOTATE(x) __attribute__((annotate(x))) +#else +#define QEMU_ANNOTATE(x) +#endif + #endif /* COMPILER_H */ diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 2897720fac..e4a4eb2d61 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -185,7 +185,7 @@ extern "C" { * } */ #ifdef __clang__ -#define coroutine_fn __attribute__((__annotate__("coroutine_fn"))) +#define coroutine_fn QEMU_ANNOTATE("coroutine_fn") #else #define coroutine_fn #endif @@ -195,7 +195,7 @@ extern "C" { * but can handle running in non-coroutine context too. */ #ifdef __clang__ -#define coroutine_mixed_fn __attribute__((__annotate__("coroutine_mixed_fn"))) +#define coroutine_mixed_fn QEMU_ANNOTATE("coroutine_mixed_fn") #else #define coroutine_mixed_fn #endif @@ -224,7 +224,7 @@ extern "C" { * } */ #ifdef __clang__ -#define no_coroutine_fn __attribute__((__annotate__("no_coroutine_fn"))) +#define no_coroutine_fn QEMU_ANNOTATE("no_coroutine_fn") #else #define no_coroutine_fn #endif From 417f8c8ebfa32823b23fed957dcbc7108cb77dea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 26 Sep 2023 17:49:17 +0200 Subject: [PATCH 72/80] audio: remove shadowed locals Signed-off-by: Paolo Bonzini --- audio/audio.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 90c7c49d11..2f47965711 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1706,7 +1706,7 @@ static AudioState *audio_init(Audiodev *dev, const char *name) size_t i; int done = 0; const char *drvname = NULL; - VMChangeStateEntry *e; + VMChangeStateEntry *vmse; AudioState *s; struct audio_driver *driver; /* silence gcc warning about uninitialized variable */ @@ -1824,8 +1824,8 @@ static AudioState *audio_init(Audiodev *dev, const char *name) s->period_ticks = dev->timer_period * (int64_t)SCALE_US; } - e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); - if (!e) { + vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); + if (!vmse) { dolog ("warning: Could not register change state handler\n" "(Audio can continue looping even after stopping the VM)\n"); } @@ -1900,10 +1900,8 @@ CaptureVoiceOut *AUD_add_capture( cap = audio_pcm_capture_find_specific(s, as); if (cap) { QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); - return cap; } else { HWVoiceOut *hw; - CaptureVoiceOut *cap; cap = g_malloc0(sizeof(*cap)); @@ -1937,8 +1935,9 @@ CaptureVoiceOut *AUD_add_capture( QLIST_FOREACH(hw, &s->hw_head_out, entries) { audio_attach_capture (hw); } - return cap; } + + return cap; } void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) From cf02f29e1e3843784630d04783e372fa541a77e5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 18 Sep 2023 14:28:15 -0300 Subject: [PATCH 73/80] migration: Fix race that dest preempt thread close too early We hit intermit CI issue on failing at migration-test over the unit test preempt/plain: qemu-system-x86_64: Unable to read from socket: Connection reset by peer Memory content inconsistency at 5b43000 first_byte = bd last_byte = bc current = 4f hit_edge = 1 ** ERROR:../tests/qtest/migration-test.c:300:check_guests_ram: assertion failed: (bad == 0) (test program exited with status code -6) Fabiano debugged into it and found that the preempt thread can quit even without receiving all the pages, which can cause guest not receiving all the pages and corrupt the guest memory. To make sure preempt thread finished receiving all the pages, we can rely on the page_requested_count being zero because preempt channel will only receive requested page faults. Note, not all the faulted pages are required to be sent via the preempt channel/thread; imagine the case when a requested page is just queued into the background main channel for migration, the src qemu will just still send it via the background channel. Here instead of spinning over reading the count, we add a condvar so the main thread can wait on it if that unusual case happened, without burning the cpu for no good reason, even if the duration is short; so even if we spin in this rare case is probably fine. It's just better to not do so. The condvar is only used when that special case is triggered. Some memory ordering trick is needed to guarantee it from happening (against the preempt thread status field), so the main thread will always get a kick when that triggers correctly. Closes: https://gitlab.com/qemu-project/qemu/-/issues/1886 Debugged-by: Fabiano Rosas Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-2-farosas@suse.de> --- migration/migration.c | 3 ++- migration/migration.h | 13 ++++++++++++- migration/postcopy-ram.c | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index d61e572742..3ee1e6b0d6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -153,6 +153,7 @@ void migration_object_init(void) qemu_sem_init(¤t_incoming->postcopy_qemufile_dst_done, 0); qemu_mutex_init(¤t_incoming->page_request_mutex); + qemu_cond_init(¤t_incoming->page_request_cond); current_incoming->page_requested = g_tree_new(page_request_addr_cmp); migration_object_check(current_migration, &error_fatal); @@ -367,7 +368,7 @@ int migrate_send_rp_req_pages(MigrationIncomingState *mis, * things like g_tree_lookup() will return TRUE (1) when found. */ g_tree_insert(mis->page_requested, aligned, (gpointer)1); - mis->page_requested_count++; + qatomic_inc(&mis->page_requested_count); trace_postcopy_page_req_add(aligned, mis->page_requested_count); } } diff --git a/migration/migration.h b/migration/migration.h index c390500604..cdaa10d515 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -196,7 +196,10 @@ struct MigrationIncomingState { /* A tree of pages that we requested to the source VM */ GTree *page_requested; - /* For debugging purpose only, but would be nice to keep */ + /* + * For postcopy only, count the number of requested page faults that + * still haven't been resolved. + */ int page_requested_count; /* * The mutex helps to maintain the requested pages that we sent to the @@ -210,6 +213,14 @@ struct MigrationIncomingState { * contains valid information. */ QemuMutex page_request_mutex; + /* + * If postcopy preempt is enabled, there is a chance that the main + * thread finished loading its data before the preempt channel has + * finished loading the urgent pages. If that happens, the two threads + * will use this condvar to synchronize, so the main thread will always + * wait until all pages received. + */ + QemuCond page_request_cond; /* * Number of devices that have yet to approve switchover. When this reaches diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 29aea9456d..5408e028c6 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -599,6 +599,30 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) if (mis->preempt_thread_status == PREEMPT_THREAD_CREATED) { /* Notify the fast load thread to quit */ mis->preempt_thread_status = PREEMPT_THREAD_QUIT; + /* + * Update preempt_thread_status before reading count. Note: mutex + * lock only provide ACQUIRE semantic, and it doesn't stops this + * write to be reordered after reading the count. + */ + smp_mb(); + /* + * It's possible that the preempt thread is still handling the last + * pages to arrive which were requested by guest page faults. + * Making sure nothing is left behind by waiting on the condvar if + * that unlikely case happened. + */ + WITH_QEMU_LOCK_GUARD(&mis->page_request_mutex) { + if (qatomic_read(&mis->page_requested_count)) { + /* + * It is guaranteed to receive a signal later, because the + * count>0 now, so it's destined to be decreased to zero + * very soon by the preempt thread. + */ + qemu_cond_wait(&mis->page_request_cond, + &mis->page_request_mutex); + } + } + /* Notify the fast load thread to quit */ if (mis->postcopy_qemufile_dst) { qemu_file_shutdown(mis->postcopy_qemufile_dst); } @@ -1277,8 +1301,20 @@ static int qemu_ufd_copy_ioctl(MigrationIncomingState *mis, void *host_addr, */ if (g_tree_lookup(mis->page_requested, host_addr)) { g_tree_remove(mis->page_requested, host_addr); - mis->page_requested_count--; + int left_pages = qatomic_dec_fetch(&mis->page_requested_count); + trace_postcopy_page_req_del(host_addr, mis->page_requested_count); + /* Order the update of count and read of preempt status */ + smp_mb(); + if (mis->preempt_thread_status == PREEMPT_THREAD_QUIT && + left_pages == 0) { + /* + * This probably means the main thread is waiting for us. + * Notify that we've finished receiving the last requested + * page. + */ + qemu_cond_signal(&mis->page_request_cond); + } } qemu_mutex_unlock(&mis->page_request_mutex); mark_postcopy_blocktime_end((uintptr_t)host_addr); From 28a8347281e24c2e7bba6d3301472eda41d4c096 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 18 Sep 2023 14:28:16 -0300 Subject: [PATCH 74/80] migration: Fix possible race when setting rp_state.error We don't need to set the rp_state.error right after a shutdown because qemu_file_shutdown() always sets the QEMUFile error, so the return path thread would have seen it and set the rp error itself. Setting the error outside of the thread is also racy because the thread could clear it after we set it. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-3-farosas@suse.de> --- migration/migration.c | 1 - 1 file changed, 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 3ee1e6b0d6..d426b69ada 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2074,7 +2074,6 @@ static int await_return_path_close_on_source(MigrationState *ms) * waiting for the destination. */ qemu_file_shutdown(ms->rp_state.from_dst_file); - mark_source_rp_bad(ms); } trace_await_return_path_close_on_source_joining(); qemu_thread_join(&ms->rp_state.rp_thread); From 639decf529793fc544c8055b82be8abe77fa48fa Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 18 Sep 2023 14:28:17 -0300 Subject: [PATCH 75/80] migration: Fix possible races when shutting down the return path We cannot call qemu_file_shutdown() on the return path file without taking the file lock. The return path thread could be running it's cleanup code and have just cleared the from_dst_file pointer. Checking ms->to_dst_file for errors could also race with migrate_fd_cleanup() which clears the to_dst_file pointer. Protect both accesses by taking the file lock. This was caught by inspection, it should be rare, but the next patches will start calling this code from other places, so let's do the correct thing. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-4-farosas@suse.de> --- migration/migration.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index d426b69ada..15b7258bb2 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2064,17 +2064,18 @@ static int open_return_path_on_source(MigrationState *ms, static int await_return_path_close_on_source(MigrationState *ms) { /* - * If this is a normal exit then the destination will send a SHUT and the - * rp_thread will exit, however if there's an error we need to cause - * it to exit. + * If this is a normal exit then the destination will send a SHUT + * and the rp_thread will exit, however if there's an error we + * need to cause it to exit. shutdown(2), if we have it, will + * cause it to unblock if it's stuck waiting for the destination. */ - if (qemu_file_get_error(ms->to_dst_file) && ms->rp_state.from_dst_file) { - /* - * shutdown(2), if we have it, will cause it to unblock if it's stuck - * waiting for the destination. - */ - qemu_file_shutdown(ms->rp_state.from_dst_file); + WITH_QEMU_LOCK_GUARD(&ms->qemu_file_lock) { + if (ms->to_dst_file && ms->rp_state.from_dst_file && + qemu_file_get_error(ms->to_dst_file)) { + qemu_file_shutdown(ms->rp_state.from_dst_file); + } } + trace_await_return_path_close_on_source_joining(); qemu_thread_join(&ms->rp_state.rp_thread); ms->rp_state.rp_thread_created = false; From 7478fb0df914f0a5ab551ff74b1df62dd250500e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 18 Sep 2023 14:28:18 -0300 Subject: [PATCH 76/80] migration: Fix possible race when shutting down to_dst_file It's not safe to call qemu_file_shutdown() on the to_dst_file without first checking for the file's presence under the lock. The cleanup of this file happens at postcopy_pause() and migrate_fd_cleanup() which are not necessarily running in the same thread as migrate_fd_cancel(). Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-5-farosas@suse.de> --- migration/migration.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 15b7258bb2..6e09463466 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1246,7 +1246,7 @@ static void migrate_fd_error(MigrationState *s, const Error *error) static void migrate_fd_cancel(MigrationState *s) { int old_state ; - QEMUFile *f = migrate_get_current()->to_dst_file; + trace_migrate_fd_cancel(); WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { @@ -1272,11 +1272,13 @@ static void migrate_fd_cancel(MigrationState *s) * If we're unlucky the migration code might be stuck somewhere in a * send/write while the network has failed and is waiting to timeout; * if we've got shutdown(2) available then we can force it to quit. - * The outgoing qemu file gets closed in migrate_fd_cleanup that is - * called in a bh, so there is no race against this cancel. */ - if (s->state == MIGRATION_STATUS_CANCELLING && f) { - qemu_file_shutdown(f); + if (s->state == MIGRATION_STATUS_CANCELLING) { + WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { + if (s->to_dst_file) { + qemu_file_shutdown(s->to_dst_file); + } + } } if (s->state == MIGRATION_STATUS_CANCELLING && s->block_inactive) { Error *local_err = NULL; @@ -1536,12 +1538,14 @@ void qmp_migrate_pause(Error **errp) { MigrationState *ms = migrate_get_current(); MigrationIncomingState *mis = migration_incoming_get_current(); - int ret; + int ret = 0; if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { /* Source side, during postcopy */ qemu_mutex_lock(&ms->qemu_file_lock); - ret = qemu_file_shutdown(ms->to_dst_file); + if (ms->to_dst_file) { + ret = qemu_file_shutdown(ms->to_dst_file); + } qemu_mutex_unlock(&ms->qemu_file_lock); if (ret) { error_setg(errp, "Failed to pause source migration"); From b3b101157d4651f12e6b3361af2de6bace7f9b4a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 18 Sep 2023 14:28:19 -0300 Subject: [PATCH 77/80] migration: Remove redundant cleanup of postcopy_qemufile_src This file is owned by the return path thread which is already doing cleanup. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-6-farosas@suse.de> --- migration/migration.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 6e09463466..4372b0fbbf 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1178,12 +1178,6 @@ static void migrate_fd_cleanup(MigrationState *s) qemu_fclose(tmp); } - if (s->postcopy_qemufile_src) { - migration_ioc_unregister_yank_from_file(s->postcopy_qemufile_src); - qemu_fclose(s->postcopy_qemufile_src); - s->postcopy_qemufile_src = NULL; - } - assert(!migration_is_active(s)); if (s->state == MIGRATION_STATUS_CANCELLING) { From d50f5dc075cbb891bfe4a9378600a4871264468a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 18 Sep 2023 14:28:20 -0300 Subject: [PATCH 78/80] migration: Consolidate return path closing code We'll start calling the await_return_path_close_on_source() function from other parts of the code, so move all of the related checks and tracepoints into it. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-7-farosas@suse.de> --- migration/migration.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 4372b0fbbf..f6c0250d33 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2061,6 +2061,14 @@ static int open_return_path_on_source(MigrationState *ms, /* Returns 0 if the RP was ok, otherwise there was an error on the RP */ static int await_return_path_close_on_source(MigrationState *ms) { + int ret; + + if (!ms->rp_state.rp_thread_created) { + return 0; + } + + trace_migration_return_path_end_before(); + /* * If this is a normal exit then the destination will send a SHUT * and the rp_thread will exit, however if there's an error we @@ -2078,7 +2086,10 @@ static int await_return_path_close_on_source(MigrationState *ms) qemu_thread_join(&ms->rp_state.rp_thread); ms->rp_state.rp_thread_created = false; trace_await_return_path_close_on_source_close(); - return ms->rp_state.error; + + ret = ms->rp_state.error; + trace_migration_return_path_end_after(ret); + return ret; } static inline void @@ -2374,20 +2385,8 @@ static void migration_completion(MigrationState *s) goto fail; } - /* - * If rp was opened we must clean up the thread before - * cleaning everything else up (since if there are no failures - * it will wait for the destination to send it's status in - * a SHUT command). - */ - if (s->rp_state.rp_thread_created) { - int rp_error; - trace_migration_return_path_end_before(); - rp_error = await_return_path_close_on_source(s); - trace_migration_return_path_end_after(rp_error); - if (rp_error) { - goto fail; - } + if (await_return_path_close_on_source(s)) { + goto fail; } if (qemu_file_get_error(s->to_dst_file)) { From ef796ee93b313ed2f0b427ef30320417387d2ad5 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 18 Sep 2023 14:28:21 -0300 Subject: [PATCH 79/80] migration: Replace the return path retry logic Replace the return path retry logic with finishing and restarting the thread. This fixes a race when resuming the migration that leads to a segfault. Currently when doing postcopy we consider that an IO error on the return path file could be due to a network intermittency. We then keep the thread alive but have it do cleanup of the 'from_dst_file' and wait on the 'postcopy_pause_rp' semaphore. When the user issues a migrate resume, a new return path is opened and the thread is allowed to continue. There's a race condition in the above mechanism. It is possible for the new return path file to be setup *before* the cleanup code in the return path thread has had a chance to run, leading to the *new* file being closed and the pointer set to NULL. When the thread is released after the resume, it tries to dereference 'from_dst_file' and crashes: Thread 7 "return path" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7fffd1dbf700 (LWP 9611)] 0x00005555560e4893 in qemu_file_get_error_obj (f=0x0, errp=0x0) at ../migration/qemu-file.c:154 154 return f->last_error; (gdb) bt #0 0x00005555560e4893 in qemu_file_get_error_obj (f=0x0, errp=0x0) at ../migration/qemu-file.c:154 #1 0x00005555560e4983 in qemu_file_get_error (f=0x0) at ../migration/qemu-file.c:206 #2 0x0000555555b9a1df in source_return_path_thread (opaque=0x555556e06000) at ../migration/migration.c:1876 #3 0x000055555602e14f in qemu_thread_start (args=0x55555782e780) at ../util/qemu-thread-posix.c:541 #4 0x00007ffff38d76ea in start_thread (arg=0x7fffd1dbf700) at pthread_create.c:477 #5 0x00007ffff35efa6f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 Here's the race (important bit is open_return_path happening before migration_release_dst_files): migration | qmp | return path --------------------------+-----------------------------+--------------------------------- qmp_migrate_pause() shutdown(ms->to_dst_file) f->last_error = -EIO migrate_detect_error() postcopy_pause() set_state(PAUSED) wait(postcopy_pause_sem) qmp_migrate(resume) migrate_fd_connect() resume = state == PAUSED open_return_path <-- TOO SOON! set_state(RECOVER) post(postcopy_pause_sem) (incoming closes to_src_file) res = qemu_file_get_error(rp) migration_release_dst_files() ms->rp_state.from_dst_file = NULL post(postcopy_pause_rp_sem) postcopy_pause_return_path_thread() wait(postcopy_pause_rp_sem) rp = ms->rp_state.from_dst_file goto retry qemu_file_get_error(rp) SIGSEGV ------------------------------------------------------------------------------------------- We can keep the retry logic without having the thread alive and waiting. The only piece of data used by it is the 'from_dst_file' and it is only allowed to proceed after a migrate resume is issued and the semaphore released at migrate_fd_connect(). Move the retry logic to outside the thread by waiting for the thread to finish before pausing the migration. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-8-farosas@suse.de> --- migration/migration.c | 60 ++++++++----------------------------------- migration/migration.h | 1 - 2 files changed, 11 insertions(+), 50 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index f6c0250d33..af78f7ee54 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1787,18 +1787,6 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, } } -/* Return true to retry, false to quit */ -static bool postcopy_pause_return_path_thread(MigrationState *s) -{ - trace_postcopy_pause_return_path(); - - qemu_sem_wait(&s->postcopy_pause_rp_sem); - - trace_postcopy_pause_return_path_continued(); - - return true; -} - static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name) { RAMBlock *block = qemu_ram_block_by_name(block_name); @@ -1882,7 +1870,6 @@ static void *source_return_path_thread(void *opaque) trace_source_return_path_thread_entry(); rcu_register_thread(); -retry: while (!ms->rp_state.error && !qemu_file_get_error(rp) && migration_is_setup_or_active(ms->state)) { trace_source_return_path_thread_loop_top(); @@ -2004,26 +1991,7 @@ retry: } out: - res = qemu_file_get_error(rp); - if (res) { - if (res && migration_in_postcopy()) { - /* - * Maybe there is something we can do: it looks like a - * network down issue, and we pause for a recovery. - */ - migration_release_dst_files(ms); - rp = NULL; - if (postcopy_pause_return_path_thread(ms)) { - /* - * Reload rp, reset the rest. Referencing it is safe since - * it's reset only by us above, or when migration completes - */ - rp = ms->rp_state.from_dst_file; - ms->rp_state.error = false; - goto retry; - } - } - + if (qemu_file_get_error(rp)) { trace_source_return_path_thread_bad_end(); mark_source_rp_bad(ms); } @@ -2034,8 +2002,7 @@ out: return NULL; } -static int open_return_path_on_source(MigrationState *ms, - bool create_thread) +static int open_return_path_on_source(MigrationState *ms) { ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file); if (!ms->rp_state.from_dst_file) { @@ -2044,11 +2011,6 @@ static int open_return_path_on_source(MigrationState *ms, trace_open_return_path_on_source(); - if (!create_thread) { - /* We're done */ - return 0; - } - qemu_thread_create(&ms->rp_state.rp_thread, "return path", source_return_path_thread, ms, QEMU_THREAD_JOINABLE); ms->rp_state.rp_thread_created = true; @@ -2088,6 +2050,7 @@ static int await_return_path_close_on_source(MigrationState *ms) trace_await_return_path_close_on_source_close(); ret = ms->rp_state.error; + ms->rp_state.error = false; trace_migration_return_path_end_after(ret); return ret; } @@ -2563,6 +2526,13 @@ static MigThrError postcopy_pause(MigrationState *s) qemu_file_shutdown(file); qemu_fclose(file); + /* + * We're already pausing, so ignore any errors on the return + * path and just wait for the thread to finish. It will be + * re-created when we resume. + */ + await_return_path_close_on_source(s); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_POSTCOPY_PAUSED); @@ -2580,12 +2550,6 @@ static MigThrError postcopy_pause(MigrationState *s) if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { /* Woken up by a recover procedure. Give it a shot */ - /* - * Firstly, let's wake up the return path now, with a new - * return path channel. - */ - qemu_sem_post(&s->postcopy_pause_rp_sem); - /* Do the resume logic */ if (postcopy_do_resume(s) == 0) { /* Let's continue! */ @@ -3275,7 +3239,7 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) * QEMU uses the return path. */ if (migrate_postcopy_ram() || migrate_return_path()) { - if (open_return_path_on_source(s, !resume)) { + if (open_return_path_on_source(s)) { error_setg(&local_err, "Unable to open return-path for postcopy"); migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); migrate_set_error(s, local_err); @@ -3339,7 +3303,6 @@ static void migration_instance_finalize(Object *obj) qemu_sem_destroy(&ms->rate_limit_sem); qemu_sem_destroy(&ms->pause_sem); qemu_sem_destroy(&ms->postcopy_pause_sem); - qemu_sem_destroy(&ms->postcopy_pause_rp_sem); qemu_sem_destroy(&ms->rp_state.rp_sem); qemu_sem_destroy(&ms->rp_state.rp_pong_acks); qemu_sem_destroy(&ms->postcopy_qemufile_src_sem); @@ -3359,7 +3322,6 @@ static void migration_instance_init(Object *obj) migrate_params_init(&ms->parameters); qemu_sem_init(&ms->postcopy_pause_sem, 0); - qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); qemu_sem_init(&ms->rp_state.rp_sem, 0); qemu_sem_init(&ms->rp_state.rp_pong_acks, 0); qemu_sem_init(&ms->rate_limit_sem, 0); diff --git a/migration/migration.h b/migration/migration.h index cdaa10d515..972597f4de 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -393,7 +393,6 @@ struct MigrationState { /* Needed by postcopy-pause state */ QemuSemaphore postcopy_pause_sem; - QemuSemaphore postcopy_pause_rp_sem; /* * Whether we abort the migration if decompression errors are * detected at the destination. It is left at false for qemu From 36e9aab3c569d4c9ad780473596e18479838d1aa Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Mon, 18 Sep 2023 14:28:22 -0300 Subject: [PATCH 80/80] migration: Move return path cleanup to main migration thread Now that the return path thread is allowed to finish during a paused migration, we can move the cleanup of the QEMUFiles to the main migration thread. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Signed-off-by: Stefan Hajnoczi Message-ID: <20230918172822.19052-9-farosas@suse.de> --- migration/migration.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index af78f7ee54..e2ed85b5be 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -98,6 +98,7 @@ static int migration_maybe_pause(MigrationState *s, int *current_active_state, int new_state); static void migrate_fd_cancel(MigrationState *s); +static int await_return_path_close_on_source(MigrationState *s); static bool migration_needs_multiple_sockets(void) { @@ -1178,6 +1179,12 @@ static void migrate_fd_cleanup(MigrationState *s) qemu_fclose(tmp); } + /* + * We already cleaned up to_dst_file, so errors from the return + * path might be due to that, ignore them. + */ + await_return_path_close_on_source(s); + assert(!migration_is_active(s)); if (s->state == MIGRATION_STATUS_CANCELLING) { @@ -1997,7 +2004,6 @@ out: } trace_source_return_path_thread_end(); - migration_release_dst_files(ms); rcu_unregister_thread(); return NULL; } @@ -2051,6 +2057,9 @@ static int await_return_path_close_on_source(MigrationState *ms) ret = ms->rp_state.error; ms->rp_state.error = false; + + migration_release_dst_files(ms); + trace_migration_return_path_end_after(ret); return ret; }