Merge tag 'next-pr-pull-request' of https://gitlab.com/berrange/qemu into staging

Merge crypto and other misc fixes / features

 * Increase minimum gnutls to 3.7.5
 * Increase minimum libgcrypt to 1.9.4
 * Increase minimum nettle to 3.7.3
 * Drop obsolete in-tree XTS impl
 * Fix memory leak when loading certificates
 * Remove/reduce duplication when loading certifcates
 * Fix possible crash when certificates are unloaded
   while an active TLS connection is using when in a
   TLS handshake operation
 * Deprecate use of dh-params.pem file
 * Document how to create certificates with Post-Quantum
   Cryptography compliant algorithms.
 * Support loading multiple certificate identities to
   allow support for Post-Quantum crypto in parallel
   with traditional RSA/ECC
 * Add "-run-with exit-with-parent=on" parameter
 * Flush pending errors when seeing ENOBUFS with
   a zero-copy send attempt
 * Fix data buffer parameters in hash & IO channel APIs
   to use 'void *'

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmkIr/8ACgkQvobrtBUQ
# T9+2RhAAhEak/krdlTJw8OlJonUop7G5mlLU2TEoX0duRORcFhScsdSwb2pyc/wM
# tnwfWXsnsKFItJx1y3STkOICtdNqizGoU3+c7wl4anQBurydu+XTs4ESBtVJtMYr
# 1lTYvp0HFyKvaXwDWKE+ztltlJiog51tHPDLUIBCnyJysLVqxCHMHmkbG46IPBZo
# A2XXxp3j/VBPmhls0JHpbAD4iVE3PChdK7zhyeGe/rld9+0JA12EPCvZ5Uokdj41
# aYP/okvnVH1atucoygPdDE3P5GYBKaSXZUWqzfkKhU7FgaF2863Td7ff1ip+WyWN
# FFPNEU1hVg+T5hfsZVQmmIFDdSJWqoZaZM/WJVYdrRY4dKUCPnJ9OINbbnhuWz5E
# JFmZOPibRZKQ44XcHX49JRfJEBvoq1z9OT1r7HkEP4D9/O7V/riIunbAESMk0sgi
# 0/fatvdhNKMN6YBQM3mtN3yNOcfRSWFtSy9XS9zDjdpEKT7ui2t9FC0ZNSP0FRkS
# aTY31FyacjHwU3zaoh6NoqqpxV9wwHrgsJwNbA/IztjmX/jvGG0Gb/sXVEqM59tR
# e3VWTmlmZ1T8OLImh1hG4t+nY+XzI64QpVX8H9RCGm21o28DyTcOnTFK4OyIfWe5
# ttnNfEJN8WCVCsA8tcM8yAbZ/0qXrYfiZSO7hq79wE7LvyholAQ=
# =9ESG
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 03 Nov 2025 02:37:03 PM CET
# gpg:                using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [unknown]
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E  8E3F BE86 EBB4 1510 4FDF

* tag 'next-pr-pull-request' of https://gitlab.com/berrange/qemu: (32 commits)
  docs: creation of x509 certs compliant with post-quantum crypto
  crypto: support upto 5 parallel certificate identities
  crypto: expand logic to cope with multiple certificate identities
  crypto: avoid loading the identity certs twice
  crypto: avoid loading the CA certs twice
  crypto: deprecate use of external dh-params.pem file
  crypto: make TLS credentials structs private
  crypto: fix lifecycle handling of gnutls credentials objects
  crypto: introduce a wrapper around gnutls credentials
  crypto: introduce method for reloading TLS creds
  crypto: reduce duplication in handling TLS priority strings
  crypto: remove duplication loading x509 CA cert
  crypto: shorten the endpoint == server check in TLS creds
  crypto: move release of DH parameters into TLS creds parent
  crypto: remove needless indirection via parent_obj field
  crypto: use g_autofree when loading x509 credentials
  crypto: move check for TLS creds 'dir' property
  crypto: remove redundant access() checks before loading certs
  crypto: replace stat() with access() for credential checks
  crypto: add missing free of certs array
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index a6a1d36..93cef88 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3908,6 +3908,17 @@
 F: tests/data/uefi-boot-images/
 F: tests/uefi-test-tools/
 
+IGVM Firmware
+M: Gerd Hoffmann <kraxel@redhat.com>
+M: Stefano Garzarella <sgarzare@redhat.com>
+R: Ani Sinha <anisinha@redhat.com>
+S: Maintained
+F: backends/igvm*.c
+F: docs/system/igvm.rst
+F: include/system/igvm*.h
+F: stubs/igvm.c
+F: target/i386/igvm.c
+
 VT-d Emulation
 M: Michael S. Tsirkin <mst@redhat.com>
 R: Jason Wang <jasowang@redhat.com>
@@ -4412,6 +4423,7 @@
 M: Thomas Huth <thuth@redhat.com>
 R: Philippe Mathieu-Daudé <philmd@linaro.org>
 R: Daniel P. Berrange <berrange@redhat.com>
+S: Maintained
 F: docs/devel/testing/functional.rst
 F: scripts/clean_functional_cache.py
 F: tests/functional/qemu_test/
diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c
index 2fb4643..f84342e 100644
--- a/accel/tcg/tcg-accel-ops-rr.c
+++ b/accel/tcg/tcg-accel-ops-rr.c
@@ -197,7 +197,7 @@
     qemu_guest_random_seed_thread_part2(cpu->random_seed);
 
     /* wait for initial kick-off after machine start */
-    while (first_cpu->stopped) {
+    while (cpu_is_stopped(first_cpu)) {
         qemu_cond_wait_bql(first_cpu->halt_cond);
 
         /* process any pending work */
diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c
index 45df63e..d00acf3 100644
--- a/backends/igvm-cfg.c
+++ b/backends/igvm-cfg.c
@@ -12,7 +12,7 @@
 #include "qemu/osdep.h"
 
 #include "system/igvm-cfg.h"
-#include "igvm.h"
+#include "system/igvm.h"
 #include "qom/object_interfaces.h"
 
 static char *get_igvm(Object *obj, Error **errp)
diff --git a/backends/igvm.c b/backends/igvm.c
index 9ad4158..905bd8d 100644
--- a/backends/igvm.c
+++ b/backends/igvm.c
@@ -11,8 +11,9 @@
 
 #include "qemu/osdep.h"
 
-#include "igvm.h"
 #include "qapi/error.h"
+#include "qemu/target-info-qapi.h"
+#include "system/igvm.h"
 #include "system/memory.h"
 #include "system/address-spaces.h"
 #include "hw/core/cpu.h"
@@ -431,18 +432,6 @@
         return 0;
     }
 
-    /*
-     * A confidential guest support object must be provided for setting
-     * a VP context.
-     */
-    if (!ctx->cgs) {
-        error_setg(
-            errp,
-            "A VP context is present in the IGVM file but is not supported "
-            "by the current system.");
-        return -1;
-    }
-
     data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
                                        ctx->current_header_index);
     if (data_handle < 0) {
@@ -452,9 +441,21 @@
     }
 
     data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
-    result = ctx->cgsc->set_guest_state(
-        vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
-        CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
+
+    if (ctx->cgs) {
+        result = ctx->cgsc->set_guest_state(
+            vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
+            CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
+    } else if (target_arch() == SYS_EMU_TARGET_X86_64) {
+        result = qigvm_x86_set_vp_context(data, vp_context->vp_index, errp);
+    } else {
+        error_setg(
+            errp,
+            "A VP context is present in the IGVM file but is not supported "
+            "by the current system.");
+        result = -1;
+    }
+
     igvm_free_buffer(ctx->file, data_handle);
     if (result < 0) {
         return result;
@@ -543,6 +544,8 @@
                                       Error **errp)
 {
     const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
+    int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *entry,
+                             Error **errp) = NULL;
     QIgvmParameterData *param_entry;
     int max_entry_count;
     int entry = 0;
@@ -550,7 +553,13 @@
     ConfidentialGuestMemoryMapEntry cgmm_entry;
     int retval = 0;
 
-    if (!ctx->cgs) {
+    if (ctx->cgs && ctx->cgsc->get_mem_map_entry) {
+        get_mem_map_entry = ctx->cgsc->get_mem_map_entry;
+
+    } else if (target_arch() == SYS_EMU_TARGET_X86_64) {
+        get_mem_map_entry = qigvm_x86_get_mem_map_entry;
+
+    } else {
         error_setg(errp,
                    "IGVM file contains a memory map but this is not supported "
                    "by the current system.");
@@ -565,9 +574,9 @@
                 param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
             mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
 
-            retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp);
+            retval = get_mem_map_entry(entry, &cgmm_entry, errp);
             while (retval == 0) {
-                if (entry > max_entry_count) {
+                if (entry >= max_entry_count) {
                     error_setg(
                         errp,
                         "IGVM: guest memory map size exceeds parameter area defined in IGVM file");
@@ -598,8 +607,7 @@
                         IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
                     break;
                 }
-                retval =
-                    ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
+                retval = get_mem_map_entry(++entry, &cgmm_entry, errp);
             }
             if (retval < 0) {
                 return retval;
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index a3bb5aa..5841dfc 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -396,7 +396,7 @@
     uint64_t subregion_size;
 
     QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
-        subregion_size = int128_get64(subregion->size);
+        subregion_size = memory_region_size(subregion);
         if ((offset >= subregion->addr) &&
             (offset + len) <= (subregion->addr + subregion_size)) {
             mr = subregion;
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index 67e15c8..e0be4ee 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -150,10 +150,6 @@
 
 int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque);
 
-/* Returns: 0 on success, -1 on error */
-int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
-                        void *ptr, size_t len, bool is_write);
-
 /* vl.c */
 void list_cpus(void);
 
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index e79e8e0..9615051 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -689,6 +689,26 @@
                              void *opaque);
 
 /**
+ * cpu_memory_rw_debug:
+ * @cpu: The CPU whose memory is to be accessed
+ * @addr: guest virtual address
+ * @ptr: buffer with the data transferred
+ * @len: the number of bytes to read or write
+ * @is_write: indicates the transfer direction
+ *
+ * Take a virtual address, convert it to a physical address via
+ * an MMU lookup using the current settings of the specified CPU,
+ * and then perform the access (using address_space_rw() for
+ * reads or address_space_write_rom() for writes).
+ *
+ * This function is intended for use by the GDB stub and similar code.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
+                        void *ptr, size_t len, bool is_write);
+
+/**
  * cpu_get_crash_info:
  * @cpu: The CPU to get crash information for
  *
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index 4e2436b..0d55c63 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -271,6 +271,24 @@
 bool bql_locked(void);
 
 /**
+ * mutex_is_bql:
+ *
+ * @mutex: the mutex pointer
+ *
+ * Returns whether the mutex is the BQL.
+ */
+bool mutex_is_bql(QemuMutex *mutex);
+
+/**
+ * bql_update_status:
+ *
+ * @locked: update status on whether the BQL is locked
+ *
+ * NOTE: this should normally only be invoked when the status changed.
+ */
+void bql_update_status(bool locked);
+
+/**
  * bql_block: Allow/deny releasing the BQL
  *
  * The Big QEMU Lock (BQL) is used to provide interior mutability to
diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index 406d741..8b561cd 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -786,11 +786,12 @@
 }
 
 /**
- * initclocks:
+ * qemu_init_clocks:
+ * @notify_cb: optional call-back for timer expiry
  *
  * Initialise the clock & timer infrastructure
  */
-void init_clocks(QEMUTimerListNotifyCB *notify_cb);
+void qemu_init_clocks(QEMUTimerListNotifyCB *notify_cb);
 
 static inline int64_t get_max_clock_jump(void)
 {
diff --git a/backends/igvm.h b/include/system/igvm.h
similarity index 64%
rename from backends/igvm.h
rename to include/system/igvm.h
index a4abab0..48ce206 100644
--- a/backends/igvm.h
+++ b/include/system/igvm.h
@@ -19,4 +19,11 @@
 int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs,
                       bool onlyVpContext, Error **errp);
 
+/* x86 native */
+int qigvm_x86_get_mem_map_entry(int index,
+                                ConfidentialGuestMemoryMapEntry *entry,
+                                Error **errp);
+int qigvm_x86_set_vp_context(void *data, int index,
+                             Error **errp);
+
 #endif
diff --git a/scripts/device-crash-test b/scripts/device-crash-test
index 1ecb966..c1576e8 100755
--- a/scripts/device-crash-test
+++ b/scripts/device-crash-test
@@ -527,7 +527,7 @@
         # Async QMP, when in use, is chatty about connection failures.
         # This script knowingly generates a ton of connection errors.
         # Silence this logger.
-        logging.getLogger('qemu.qmp.qmp_client').setLevel(logging.CRITICAL)
+        logging.getLogger('qemu.qmp.protocol').setLevel(logging.CRITICAL)
 
     fatal_failures = []
     wl_stats = {}
diff --git a/stubs/igvm.c b/stubs/igvm.c
new file mode 100644
index 0000000..17cd1e9
--- /dev/null
+++ b/stubs/igvm.c
@@ -0,0 +1,26 @@
+/*
+ * QEMU IGVM, stubs
+ *
+ * Copyright (C) 2026 Red Hat
+ *
+ * Authors:
+ *  Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "system/igvm.h"
+
+int qigvm_x86_get_mem_map_entry(int index,
+                                ConfidentialGuestMemoryMapEntry *entry,
+                                Error **errp)
+{
+    return -1;
+}
+
+int qigvm_x86_set_vp_context(void *data, int index, Error **errp)
+{
+    return -1;
+}
diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c
index 6050c08..c89c9c7 100644
--- a/stubs/iothread-lock.c
+++ b/stubs/iothread-lock.c
@@ -34,3 +34,12 @@
     assert((new_value > bql_unlock_blocked) == increase);
     bql_unlock_blocked = new_value;
 }
+
+bool mutex_is_bql(QemuMutex *mutex)
+{
+    return false;
+}
+
+void bql_update_status(bool locked)
+{
+}
diff --git a/stubs/meson.build b/stubs/meson.build
index 5d57746..27be2de 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -74,6 +74,7 @@
   stub_ss.add(files('dump.c'))
   stub_ss.add(files('cmos.c'))
   stub_ss.add(files('fw_cfg.c'))
+  stub_ss.add(files('igvm.c'))
   stub_ss.add(files('target-get-monitor-def.c'))
   stub_ss.add(files('target-monitor-defs.c'))
   stub_ss.add(files('win32-kbd-hook.c'))
diff --git a/system/cpus.c b/system/cpus.c
index aa7bfcf..ef2d2f2 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -480,10 +480,10 @@
 
 void cpus_kick_thread(CPUState *cpu)
 {
-    if (cpu->thread_kicked) {
+    if (qatomic_read(&cpu->thread_kicked)) {
         return;
     }
-    cpu->thread_kicked = true;
+    qatomic_set(&cpu->thread_kicked, true);
 
 #ifndef _WIN32
     int err = pthread_kill(cpu->thread->thread, SIG_IPI);
@@ -524,6 +524,18 @@
 
 QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked)
 
+bool mutex_is_bql(QemuMutex *mutex)
+{
+    return mutex == &bql;
+}
+
+void bql_update_status(bool locked)
+{
+    /* This function should only be used when an update happened.. */
+    assert(bql_locked() != locked);
+    set_bql_locked(locked);
+}
+
 static uint32_t bql_unlock_blocked;
 
 void bql_block_unlock(bool increase)
@@ -564,14 +576,12 @@
 
     g_assert(!bql_locked());
     bql_lock_fn(&bql, file, line);
-    set_bql_locked(true);
 }
 
 void bql_unlock(void)
 {
     g_assert(bql_locked());
     g_assert(!bql_unlock_blocked);
-    set_bql_locked(false);
     qemu_mutex_unlock(&bql);
 }
 
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 0a66e1f..6417775 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -8770,6 +8770,12 @@
 
     cs->halted = !cpu_is_bsp(cpu);
 
+#if defined(CONFIG_IGVM)
+    if (cpu_is_bsp(cpu)) {
+        qigvm_x86_bsp_reset(env);
+    }
+#endif
+
     if (kvm_enabled()) {
         kvm_arch_reset_vcpu(cpu);
     }
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index d0da9bf..cee1f69 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2868,6 +2868,9 @@
 
 #endif
 
+/* igvm.c */
+void qigvm_x86_bsp_reset(CPUX86State *env);
+
 /* cpu.c */
 bool cpu_is_bsp(X86CPU *cpu);
 
diff --git a/target/i386/igvm.c b/target/i386/igvm.c
new file mode 100644
index 0000000..457c253
--- /dev/null
+++ b/target/i386/igvm.c
@@ -0,0 +1,180 @@
+/*
+ * QEMU IGVM, support for native x86 guests
+ *
+ * Copyright (C) 2026 Red Hat
+ *
+ * Authors:
+ *  Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "hw/i386/e820_memory_layout.h"
+#include "system/igvm.h"
+
+struct IgvmNativeVpContextX64 {
+    uint64_t rax;
+    uint64_t rcx;
+    uint64_t rdx;
+    uint64_t rbx;
+    uint64_t rsp;
+    uint64_t rbp;
+    uint64_t rsi;
+    uint64_t rdi;
+    uint64_t r8;
+    uint64_t r9;
+    uint64_t r10;
+    uint64_t r11;
+    uint64_t r12;
+    uint64_t r13;
+    uint64_t r14;
+    uint64_t r15;
+    uint64_t rip;
+    uint64_t rflags;
+    uint64_t idtr_base;
+    uint16_t idtr_limit;
+    uint16_t reserved[2];
+    uint16_t gdtr_limit;
+    uint64_t gdtr_base;
+
+    uint16_t code_selector;
+    uint16_t code_attributes;
+    uint32_t code_base;
+    uint32_t code_limit;
+
+    uint16_t data_selector;
+    uint16_t data_attributes;
+    uint32_t data_base;
+    uint32_t data_limit;
+
+    uint64_t gs_base;
+    uint64_t cr0;
+    uint64_t cr3;
+    uint64_t cr4;
+    uint64_t efer;
+};
+
+#define FLAGS_TO_SEGCACHE(flags)                \
+    (((unsigned int)flags) << 8)
+
+static void qigvm_x86_load_context(struct IgvmNativeVpContextX64 *context,
+                                   CPUX86State *env)
+{
+    cpu_load_efer(env, context->efer);
+    cpu_x86_update_cr4(env, context->cr4);
+    cpu_x86_update_cr0(env, context->cr0);
+    cpu_x86_update_cr3(env, context->cr3);
+
+    cpu_x86_load_seg_cache(
+        env, R_CS, context->code_selector,
+        context->code_base, context->code_limit,
+        FLAGS_TO_SEGCACHE(context->code_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_DS, context->data_selector,
+        context->data_base, context->data_limit,
+        FLAGS_TO_SEGCACHE(context->data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_ES, context->data_selector,
+        context->data_base, context->data_limit,
+        FLAGS_TO_SEGCACHE(context->data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_FS, context->data_selector,
+        context->data_base, context->data_limit,
+        FLAGS_TO_SEGCACHE(context->data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_GS, context->data_selector,
+        context->data_base, context->data_limit,
+        FLAGS_TO_SEGCACHE(context->data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_SS, context->data_selector,
+        context->data_base, context->data_limit,
+        FLAGS_TO_SEGCACHE(context->data_attributes));
+
+    env->gdt.base = context->gdtr_base;
+    env->gdt.limit = context->gdtr_limit;
+    env->idt.base = context->idtr_base;
+    env->idt.limit = context->idtr_limit;
+
+    env->regs[R_EAX] = context->rax;
+    env->regs[R_ECX] = context->rcx;
+    env->regs[R_EDX] = context->rdx;
+    env->regs[R_EBX] = context->rbx;
+    env->regs[R_ESP] = context->rsp;
+    env->regs[R_EBP] = context->rbp;
+    env->regs[R_ESI] = context->rsi;
+    env->regs[R_EDI] = context->rdi;
+#ifdef TARGET_X86_64
+    env->regs[R_R8] = context->r8;
+    env->regs[R_R9] = context->r9;
+    env->regs[R_R10] = context->r10;
+    env->regs[R_R11] = context->r11;
+    env->regs[R_R12] = context->r12;
+    env->regs[R_R13] = context->r13;
+    env->regs[R_R14] = context->r14;
+    env->regs[R_R15] = context->r15;
+#endif
+    env->eip = context->rip;
+    env->eflags = context->rflags;
+}
+
+/*
+ * convert e820 table into igvm memory map
+ */
+int qigvm_x86_get_mem_map_entry(int index,
+                                ConfidentialGuestMemoryMapEntry *entry,
+                                Error **errp)
+{
+    struct e820_entry *table;
+    int num_entries;
+
+    num_entries = e820_get_table(&table);
+    if ((index < 0) || (index >= num_entries)) {
+        return 1;
+    }
+    entry->gpa = table[index].address;
+    entry->size = table[index].length;
+    switch (table[index].type) {
+    case E820_RAM:
+        entry->type = CGS_MEM_RAM;
+        break;
+    case E820_RESERVED:
+        entry->type = CGS_MEM_RESERVED;
+        break;
+    default:
+        /* should not happen */
+        error_setg(errp, "unknown e820 type");
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * set initial cpu context
+ */
+static struct IgvmNativeVpContextX64 *bsp_context;
+
+int qigvm_x86_set_vp_context(void *data, int index, Error **errp)
+{
+    if (index != 0) {
+        error_setg(errp, "context can be set for BSP only");
+        return -1;
+    }
+
+    if (bsp_context == NULL) {
+        bsp_context = g_new0(struct IgvmNativeVpContextX64, 1);
+    }
+    memcpy(bsp_context, data, sizeof(struct IgvmNativeVpContextX64));
+    return 0;
+}
+
+void qigvm_x86_bsp_reset(CPUX86State *env)
+{
+    if (bsp_context == NULL) {
+        return;
+    }
+
+    qigvm_x86_load_context(bsp_context, env);
+}
diff --git a/target/i386/meson.build b/target/i386/meson.build
index 89ba491..d385eaf 100644
--- a/target/i386/meson.build
+++ b/target/i386/meson.build
@@ -26,6 +26,9 @@
 ))
 i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'),
                                        if_false: files('sev-system-stub.c'))
+if igvm.found()
+  i386_system_ss.add(files('igvm.c'))
+endif
 
 i386_user_ss = ss.source_set()
 
diff --git a/target/rx/helper.c b/target/rx/helper.c
index ef47e32..e9a7aaf 100644
--- a/target/rx/helper.c
+++ b/target/rx/helper.c
@@ -41,11 +41,9 @@
     env->psw_c = FIELD_EX32(psw, PSW, C);
 }
 
-#define INT_FLAGS (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR)
 void rx_cpu_do_interrupt(CPUState *cs)
 {
     CPURXState *env = cpu_env(cs);
-    int do_irq = cpu_test_interrupt(cs, INT_FLAGS);
     uint32_t save_psw;
     uint64_t last_pc = env->pc;
 
@@ -59,29 +57,26 @@
     save_psw = rx_cpu_pack_psw(env);
     env->psw_pm = env->psw_i = env->psw_u = 0;
 
-    if (do_irq) {
-        if (do_irq & CPU_INTERRUPT_FIR) {
-            env->bpc = env->pc;
-            env->bpsw = save_psw;
-            env->pc = env->fintv;
-            env->psw_ipl = 15;
-            cpu_reset_interrupt(cs, CPU_INTERRUPT_FIR);
-            qemu_set_irq(env->ack, env->ack_irq);
-            qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
-            qemu_log_mask(CPU_LOG_INT, "fast interrupt raised\n");
-        } else if (do_irq & CPU_INTERRUPT_HARD) {
-            env->isp -= 4;
-            cpu_stl_data(env, env->isp, save_psw);
-            env->isp -= 4;
-            cpu_stl_data(env, env->isp, env->pc);
-            env->pc = cpu_ldl_data(env, env->intb + env->ack_irq * 4);
-            env->psw_ipl = env->ack_ipl;
-            cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
-            qemu_set_irq(env->ack, env->ack_irq);
-            qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
-            qemu_log_mask(CPU_LOG_INT,
-                          "interrupt 0x%02x raised\n", env->ack_irq);
-        }
+    if (cpu_test_interrupt(cs, CPU_INTERRUPT_FIR)) {
+        env->bpc = env->pc;
+        env->bpsw = save_psw;
+        env->pc = env->fintv;
+        env->psw_ipl = 15;
+        cpu_reset_interrupt(cs, CPU_INTERRUPT_FIR);
+        qemu_set_irq(env->ack, env->ack_irq);
+        qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
+        qemu_log_mask(CPU_LOG_INT, "fast interrupt raised\n");
+    } else if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) {
+        env->isp -= 4;
+        cpu_stl_data(env, env->isp, save_psw);
+        env->isp -= 4;
+        cpu_stl_data(env, env->isp, env->pc);
+        env->pc = cpu_ldl_data(env, env->intb + env->ack_irq * 4);
+        env->psw_ipl = env->ack_ipl;
+        cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+        qemu_set_irq(env->ack, env->ack_irq);
+        qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
+        qemu_log_mask(CPU_LOG_INT, "interrupt 0x%02x raised\n", env->ack_irq);
     } else {
         uint32_t vec = cs->exception_index;
         const char *expname = "unknown exception";
diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
index f1acb16..24675fc 100644
--- a/target/s390x/tcg/mem_helper.c
+++ b/target/s390x/tcg/mem_helper.c
@@ -1959,6 +1959,10 @@
         if (env->cregs[i] != val && i >= 9 && i <= 11) {
             PERchanged = true;
         }
+        if (i == 0 && !(env->cregs[i] & CR0_CKC_SC) && (val & CR0_CKC_SC)) {
+            BQL_LOCK_GUARD();
+            tcg_s390_tod_updated(env_cpu(env), RUN_ON_CPU_NULL);
+        }
         env->cregs[i] = val;
         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
                    i, src, val);
@@ -1989,10 +1993,15 @@
 
     for (i = r1;; i = (i + 1) % 16) {
         uint32_t val = cpu_ldl_data_ra(env, src, ra);
+        uint64_t val64 = deposit64(env->cregs[i], 0, 32, val);
         if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
             PERchanged = true;
         }
-        env->cregs[i] = deposit64(env->cregs[i], 0, 32, val);
+        if (i == 0 && !(env->cregs[i] & CR0_CKC_SC) && (val64 & CR0_CKC_SC)) {
+            BQL_LOCK_GUARD();
+            tcg_s390_tod_updated(env_cpu(env), RUN_ON_CPU_NULL);
+        }
+        env->cregs[i] = val64;
         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%x\n", i, src, val);
         src += sizeof(uint32_t);
 
diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c
index 6d9d601..215b5b9 100644
--- a/target/s390x/tcg/misc_helper.c
+++ b/target/s390x/tcg/misc_helper.c
@@ -199,11 +199,15 @@
         return;
     }
 
-    /* difference between origins */
-    time = env->ckc - td->base.low;
+    if (env->ckc < td->base.low) {
+        time = 0;
+    } else {
+        /* difference between origins */
+        time = env->ckc - td->base.low;
 
-    /* nanoseconds */
-    time = tod2time(time);
+        /* nanoseconds */
+        time = tod2time(time);
+    }
 
     timer_mod(env->tod_timer, time);
 }
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index ec9e5a0..4d2b8c5 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -5613,6 +5613,7 @@
     int r2 = get_field(s, r2);
     if (r2 != 0) {
         o->in2 = load_reg(r2);
+        gen_addi_and_wrap_i64(s, o->in2, o->in2, 0);
     }
 }
 #define SPEC_in2_r2_nz 0
@@ -6379,10 +6380,12 @@
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
 
-    /* 31-bit mode */
-    if (!(dc->base.tb->flags & FLAG_MASK_64)) {
-        dc->base.pc_first &= 0x7fffffff;
-        dc->base.pc_next = dc->base.pc_first;
+    if (dc->base.tb->flags & FLAG_MASK_32) {
+        if (!(dc->base.tb->flags & FLAG_MASK_64)) {
+            assert(!(dc->base.pc_first & ~((1ULL << 31) - 1)));
+        }
+    } else {
+        assert(!(dc->base.pc_first & ~((1ULL << 24) - 1)));
     }
 
     dc->cc_op = CC_OP_DYNAMIC;
diff --git a/tests/functional/arm/test_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py
index 60e2b02..d1822ed 100755
--- a/tests/functional/arm/test_aspeed_ast1030.py
+++ b/tests/functional/arm/test_aspeed_ast1030.py
@@ -6,9 +6,8 @@
 #
 # SPDX-License-Identifier: GPL-2.0-or-later
 
-from qemu_test import LinuxKernelTest, Asset
 from aspeed import AspeedTest
-from qemu_test import exec_command_and_wait_for_pattern
+from qemu_test import Asset, exec_command_and_wait_for_pattern
 
 
 class AST1030Machine(AspeedTest):
diff --git a/tests/functional/migration.py b/tests/functional/migration.py
index 0739554..2bfb1f7 100644
--- a/tests/functional/migration.py
+++ b/tests/functional/migration.py
@@ -30,11 +30,11 @@ def assert_migration(self, src_vm, dst_vm):
 
         end = time.monotonic() + self.timeout
         while time.monotonic() < end and not self.migration_finished(src_vm):
-           time.sleep(0.1)
+            time.sleep(0.1)
 
         end = time.monotonic() + self.timeout
         while time.monotonic() < end and not self.migration_finished(dst_vm):
-           time.sleep(0.1)
+            time.sleep(0.1)
 
         self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed')
         self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed')
diff --git a/tests/functional/ppc/test_74xx.py b/tests/functional/ppc/test_74xx.py
index 5386016..219c799 100755
--- a/tests/functional/ppc/test_74xx.py
+++ b/tests/functional/ppc/test_74xx.py
@@ -10,7 +10,7 @@
 from qemu_test import QemuSystemTest
 from qemu_test import wait_for_console_pattern
 
-class ppc74xxCpu(QemuSystemTest):
+class Ppc74xxCpu(QemuSystemTest):
 
     timeout = 5
 
diff --git a/tests/functional/ppc/test_sam460ex.py b/tests/functional/ppc/test_sam460ex.py
index 31cf9dd..024406d 100755
--- a/tests/functional/ppc/test_sam460ex.py
+++ b/tests/functional/ppc/test_sam460ex.py
@@ -8,10 +8,11 @@
 from qemu_test import exec_command_and_wait_for_pattern
 
 
-class sam460exTest(LinuxKernelTest):
+class Sam460exTest(LinuxKernelTest):
 
     ASSET_BR2_SAM460EX_LINUX = Asset(
-        'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc_sam460ex-2023.11-8-gdcd9f0f6eb-20240105/vmlinux',
+        ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main'
+         '/buildroot/qemu_ppc_sam460ex-2023.11-8-gdcd9f0f6eb-20240105/vmlinux'),
         '6f46346f3e20e8b5fc050ff363f350f8b9d76a051b9e0bd7ea470cc680c14df2')
 
     def test_ppc_sam460ex_buildroot(self):
diff --git a/tests/functional/ppc64/test_mac99.py b/tests/functional/ppc64/test_mac99.py
index dfd9c01..a3261a8 100755
--- a/tests/functional/ppc64/test_mac99.py
+++ b/tests/functional/ppc64/test_mac99.py
@@ -7,14 +7,16 @@
 from qemu_test import LinuxKernelTest, Asset
 from qemu_test import exec_command_and_wait_for_pattern
 
-class mac99Test(LinuxKernelTest):
+class Mac99Test(LinuxKernelTest):
 
     ASSET_BR2_MAC99_LINUX = Asset(
-        'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/vmlinux',
+        ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main'
+         '/buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/vmlinux'),
         'd59307437e4365f2cced0bbd1b04949f7397b282ef349b7cafd894d74aadfbff')
 
     ASSET_BR2_MAC99_ROOTFS = Asset(
-        'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/rootfs.ext2',
+        ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main'
+         '/buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/rootfs.ext2'),
         'bbd5fd8af62f580bc4e585f326fe584e22856572633a8333178ea6d4ed4955a4')
 
     def test_ppc64_mac99_buildroot(self):
diff --git a/tests/functional/ppc64/test_powernv.py b/tests/functional/ppc64/test_powernv.py
index 9ada832..0ea6c93 100755
--- a/tests/functional/ppc64/test_powernv.py
+++ b/tests/functional/ppc64/test_powernv.py
@@ -10,7 +10,7 @@
 from qemu_test import LinuxKernelTest, Asset
 from qemu_test import wait_for_console_pattern
 
-class powernvMachine(LinuxKernelTest):
+class PowernvMachine(LinuxKernelTest):
 
     timeout = 90
     KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
diff --git a/tests/functional/ppc64/test_pseries.py b/tests/functional/ppc64/test_pseries.py
index 6705793..7840c4e 100755
--- a/tests/functional/ppc64/test_pseries.py
+++ b/tests/functional/ppc64/test_pseries.py
@@ -10,7 +10,7 @@
 from qemu_test import QemuSystemTest, Asset
 from qemu_test import wait_for_console_pattern
 
-class pseriesMachine(QemuSystemTest):
+class PseriesMachine(QemuSystemTest):
 
     timeout = 90
     KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
diff --git a/tests/functional/ppc64/test_reverse_debug.py b/tests/functional/ppc64/test_reverse_debug.py
index 69551fb..4eef779 100755
--- a/tests/functional/ppc64/test_reverse_debug.py
+++ b/tests/functional/ppc64/test_reverse_debug.py
@@ -18,7 +18,7 @@
 from reverse_debugging import ReverseDebugging
 
 
-class ReverseDebugging_ppc64(ReverseDebugging):
+class ReverseDebuggingPpc64(ReverseDebugging):
 
     @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992")
     def test_ppc64_pseries(self):
diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py
index ab3a7bb..bae4076 100644
--- a/tests/functional/qemu_test/asset.py
+++ b/tests/functional/qemu_test/asset.py
@@ -112,7 +112,7 @@ def _wait_for_other_download(self, tmp_cache_file):
                 return False
 
         self.log.debug("Time out while waiting for %s!", tmp_cache_file)
-        raise
+        raise TimeoutError(f"Time out while waiting for {tmp_cache_file}")
 
     def _save_time_stamp(self):
         '''
@@ -141,7 +141,7 @@ def fetch(self):
         self.log.info("Downloading %s to %s...", self.url, self.cache_file)
         tmp_cache_file = self.cache_file.with_suffix(".download")
 
-        for retries in range(3):
+        for _retries in range(3):
             try:
                 with tmp_cache_file.open("xb") as dst:
                     with urllib.request.urlopen(self.url) as resp:
@@ -181,7 +181,7 @@ def fetch(self):
                 # server or networking problem
                 if e.code == 404:
                     raise AssetError(self, "Unable to download: "
-                                     "HTTP error %d" % e.code)
+                                     "HTTP error %d" % e.code) from e
                 continue
             except URLError as e:
                 # This is typically a network/service level error
@@ -190,7 +190,7 @@ def fetch(self):
                 self.log.error("Unable to download %s: URL error %s",
                                self.url, e.reason)
                 raise AssetError(self, "Unable to download: URL error %s" %
-                                 e.reason, transient=True)
+                                 e.reason, transient=True) from e
             except ConnectionError as e:
                 # A socket connection failure, such as dropped conn
                 # or refused conn
@@ -201,7 +201,7 @@ def fetch(self):
             except Exception as e:
                 tmp_cache_file.unlink()
                 raise AssetError(self, "Unable to download: %s" % e,
-                                 transient=True)
+                                 transient=True) from e
 
         if not os.path.exists(tmp_cache_file):
             raise AssetError(self, "Download retries exceeded", transient=True)
@@ -214,7 +214,6 @@ def fetch(self):
                         self.hash.encode('utf8'))
         except Exception as e:
             self.log.debug("Unable to set xattr on %s: %s", tmp_cache_file, e)
-            pass
 
         if not self._check(tmp_cache_file):
             tmp_cache_file.unlink()
@@ -224,9 +223,10 @@ def fetch(self):
         # Remove write perms to stop tests accidentally modifying them
         os.chmod(self.cache_file, stat.S_IRUSR | stat.S_IRGRP)
 
-        self.log.info("Cached %s at %s" % (self.url, self.cache_file))
+        self.log.info("Cached %s at %s", self.url, self.cache_file)
         return str(self.cache_file)
 
+    @staticmethod
     def precache_test(test):
         log = logging.getLogger('qemu-test')
         log.setLevel(logging.DEBUG)
@@ -237,16 +237,17 @@ def precache_test(test):
         handler.setFormatter(formatter)
         log.addHandler(handler)
         for name, asset in vars(test.__class__).items():
-            if name.startswith("ASSET_") and type(asset) == Asset:
+            if name.startswith("ASSET_") and isinstance(asset, Asset):
                 try:
                     asset.fetch()
                 except AssetError as e:
                     if not e.transient:
                         raise
-                    log.error("%s: skipping asset precache" % e)
+                    log.error("%s: skipping asset precache", e)
 
         log.removeHandler(handler)
 
+    @staticmethod
     def precache_suite(suite):
         for test in suite:
             if isinstance(test, unittest.TestSuite):
@@ -254,9 +255,10 @@ def precache_suite(suite):
             elif isinstance(test, unittest.TestCase):
                 Asset.precache_test(test)
 
-    def precache_suites(path, cacheTstamp):
+    @staticmethod
+    def precache_suites(path, cache_tstamp):
         loader = unittest.loader.defaultTestLoader
         tests = loader.loadTestsFromNames([path], None)
 
-        with open(cacheTstamp, "w") as fh:
+        with open(cache_tstamp, "w", encoding='utf-8'):
             Asset.precache_suite(tests)
diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py
index b239295..8074183 100644
--- a/tests/functional/qemu_test/decorators.py
+++ b/tests/functional/qemu_test/decorators.py
@@ -10,136 +10,134 @@
 
 from .cmd import which
 
-'''
-Decorator to skip execution of a test if the provided
-environment variables are not set.
-Example:
 
-  @skipIfMissingEnv("QEMU_ENV_VAR0", "QEMU_ENV_VAR1")
-'''
 def skipIfMissingEnv(*vars_):
+    '''
+    Decorator to skip execution of a test if the provided
+    environment variables are not set.
+    Example:
+
+      @skipIfMissingEnv("QEMU_ENV_VAR0", "QEMU_ENV_VAR1")
+    '''
     missing_vars = []
     for var in vars_:
-        if os.getenv(var) == None:
+        if os.getenv(var) is None:
             missing_vars.append(var)
 
-    has_vars = True if len(missing_vars) == 0 else False
+    has_vars = len(missing_vars) == 0
 
     return skipUnless(has_vars, f"Missing env var(s): {', '.join(missing_vars)}")
 
-'''
-
-Decorator to skip execution of a test if the list
-of command binaries is not available in $PATH.
-Example:
-
-  @skipIfMissingCommands("mkisofs", "losetup")
-'''
 def skipIfMissingCommands(*args):
+    '''
+    Decorator to skip execution of a test if the list
+    of command binaries is not available in $PATH.
+    Example:
+
+      @skipIfMissingCommands("mkisofs", "losetup")
+    '''
     has_cmds = True
     for cmd in args:
-         if not which(cmd):
-             has_cmds = False
-             break
+        if not which(cmd):
+            has_cmds = False
+            break
 
     return skipUnless(has_cmds, 'required command(s) "%s" not installed' %
                                 ", ".join(args))
 
-'''
-Decorator to skip execution of a test if the current
-host operating system does match one of the prohibited
-ones.
-Example
-
-  @skipIfOperatingSystem("Linux", "Darwin")
-'''
 def skipIfOperatingSystem(*args):
+    '''
+    Decorator to skip execution of a test if the current host
+    operating system does match one of the prohibited ones.
+    Example:
+
+      @skipIfOperatingSystem("Linux", "Darwin")
+    '''
     return skipIf(platform.system() in args,
                   'running on an OS (%s) that is not able to run this test' %
                   ", ".join(args))
 
-'''
-Decorator to skip execution of a test if the current
-host machine does not match one of the permitted
-machines.
-Example
-
-  @skipIfNotMachine("x86_64", "aarch64")
-'''
 def skipIfNotMachine(*args):
+    '''
+    Decorator to skip execution of a test if the current
+    host machine does not match one of the permitted machines.
+    Example:
+
+      @skipIfNotMachine("x86_64", "aarch64")
+    '''
     return skipUnless(platform.machine() in args,
                       'not running on one of the required machine(s) "%s"' %
                       ", ".join(args))
 
-'''
-Decorator to skip execution of flaky tests, unless
-the $QEMU_TEST_FLAKY_TESTS environment variable is set.
-A bug URL must be provided that documents the observed
-failure behaviour, so it can be tracked & re-evaluated
-in future.
-
-Historical tests may be providing "None" as the bug_url
-but this should not be done for new test.
-
-Example:
-
-  @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/NNN")
-'''
 def skipFlakyTest(bug_url):
+    '''
+    Decorator to skip execution of flaky tests, unless
+    the $QEMU_TEST_FLAKY_TESTS environment variable is set.
+    A bug URL must be provided that documents the observed
+    failure behaviour, so it can be tracked & re-evaluated
+    in future.
+
+    Historical tests may be providing "None" as the bug_url
+    but this should not be done for new test.
+
+    Example:
+
+      @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/NNN")
+    '''
     if bug_url is None:
         bug_url = "FIXME: reproduce flaky test and file bug report or remove"
     return skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'),
                       f'Test is unstable: {bug_url}')
 
-'''
-Decorator to skip execution of tests which are likely
-to execute untrusted commands on the host, or commands
-which process untrusted code, unless the
-$QEMU_TEST_ALLOW_UNTRUSTED_CODE env var is set.
-Example:
-
-  @skipUntrustedTest()
-'''
 def skipUntrustedTest():
+    '''
+    Decorator to skip execution of tests which are likely
+    to execute untrusted commands on the host, or commands
+    which process untrusted code, unless the
+    $QEMU_TEST_ALLOW_UNTRUSTED_CODE env var is set.
+    Example:
+
+      @skipUntrustedTest()
+    '''
     return skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'),
                       'Test runs untrusted code / processes untrusted data')
 
-'''
-Decorator to skip execution of tests which need large
-data storage (over around 500MB-1GB mark) on the host,
-unless the $QEMU_TEST_ALLOW_LARGE_STORAGE environment
-variable is set
-
-Example:
-
-  @skipBigDataTest()
-'''
 def skipBigDataTest():
+    '''
+    Decorator to skip execution of tests which need large
+    data storage (over around 500MB-1GB mark) on the host,
+    unless the $QEMU_TEST_ALLOW_LARGE_STORAGE environment
+    variable is set
+
+    Example:
+
+      @skipBigDataTest()
+    '''
     return skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'),
                       'Test requires large host storage space')
 
-'''
-Decorator to skip execution of tests which have a really long
-runtime (and might e.g. time out if QEMU has been compiled with
-debugging enabled) unless the $QEMU_TEST_ALLOW_SLOW
-environment variable is set
-
-Example:
-
-  @skipSlowTest()
-'''
 def skipSlowTest():
+    '''
+    Decorator to skip execution of tests which have a really long
+    runtime (and might e.g. time out if QEMU has been compiled with
+    debugging enabled) unless the $QEMU_TEST_ALLOW_SLOW
+    environment variable is set
+
+    Example:
+
+      @skipSlowTest()
+    '''
     return skipUnless(os.getenv('QEMU_TEST_ALLOW_SLOW'),
                       'Test has a very long runtime and might time out')
 
-'''
-Decorator to skip execution of a test if the list
-of python imports is not available.
-Example:
-
-  @skipIfMissingImports("numpy", "cv2")
-'''
 def skipIfMissingImports(*args):
+    '''
+    Decorator to skip execution of a test if the list
+    of python imports is not available.
+    Example:
+
+      @skipIfMissingImports("numpy", "cv2")
+    '''
     has_imports = True
     for impname in args:
         try:
@@ -151,15 +149,15 @@ def skipIfMissingImports(*args):
     return skipUnless(has_imports, 'required import(s) "%s" not installed' %
                                    ", ".join(args))
 
-'''
-Decorator to skip execution of a test if the system's
-locked memory limit is below the required threshold.
-Takes required locked memory threshold in kB.
-Example:
-
-  @skipLockedMemoryTest(2_097_152)
-'''
 def skipLockedMemoryTest(locked_memory):
+    '''
+    Decorator to skip execution of a test if the system's
+    locked memory limit is below the required threshold.
+    Takes required locked memory threshold in kB.
+    Example:
+
+      @skipLockedMemoryTest(2_097_152)
+    '''
     # get memlock hard limit in bytes
     _, ulimit_memory = resource.getrlimit(resource.RLIMIT_MEMLOCK)
 
diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py
index c476752..eb10a81 100644
--- a/tests/functional/qemu_test/linuxkernel.py
+++ b/tests/functional/qemu_test/linuxkernel.py
@@ -83,12 +83,12 @@ def launch_kernel(self, kernel, initrd=None, dtb=None, console_index=0,
         self.vm.set_console(console_index=console_index)
         self.vm.add_args('-kernel', kernel)
         if initrd:
-                self.vm.add_args('-initrd', initrd)
+            self.vm.add_args('-initrd', initrd)
         if dtb:
-                self.vm.add_args('-dtb', dtb)
+            self.vm.add_args('-dtb', dtb)
         self.vm.launch()
         if wait_for:
-                self.wait_for_console_pattern(wait_for)
+            self.wait_for_console_pattern(wait_for)
 
     def check_http_download(self, filename, hashsum, guestport=8080,
                             pythoncmd='python3 -m http.server'):
diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py
index 2c0abde..1d773dd 100644
--- a/tests/functional/qemu_test/testcase.py
+++ b/tests/functional/qemu_test/testcase.py
@@ -217,7 +217,7 @@ def setUp(self):
         self._log_fh = logging.FileHandler(self.log_filename, mode='w')
         self._log_fh.setLevel(logging.DEBUG)
         fileFormatter = logging.Formatter(
-            '%(asctime)s - %(levelname)s: %(message)s')
+            '%(asctime)s - %(levelname)s: %(name)s.%(funcName)s %(message)s')
         self._log_fh.setFormatter(fileFormatter)
         self.log.addHandler(self._log_fh)
 
@@ -225,6 +225,9 @@ def setUp(self):
         self.machinelog = logging.getLogger('qemu.machine')
         self.machinelog.setLevel(logging.DEBUG)
         self.machinelog.addHandler(self._log_fh)
+        self.qmplog = logging.getLogger('qemu.qmp')
+        self.qmplog.setLevel(logging.DEBUG)
+        self.qmplog.addHandler(self._log_fh)
 
         if not self.assets_available():
             self.skipTest('One or more assets is not available')
@@ -233,8 +236,9 @@ def tearDown(self):
         if "QEMU_TEST_KEEP_SCRATCH" not in os.environ:
             shutil.rmtree(self.workdir)
         if self.socketdir is not None:
-            shutil.rmtree(self.socketdir.name)
+            self.socketdir.cleanup()
             self.socketdir = None
+        self.qmplog.removeHandler(self._log_fh)
         self.machinelog.removeHandler(self._log_fh)
         self.log.removeHandler(self._log_fh)
         self._log_fh.close()
diff --git a/tests/functional/qemu_test/uncompress.py b/tests/functional/qemu_test/uncompress.py
index b7ef8f7..5bbdf8f 100644
--- a/tests/functional/qemu_test/uncompress.py
+++ b/tests/functional/qemu_test/uncompress.py
@@ -58,20 +58,20 @@ def zstd_uncompress(zstd_path, output_path):
     os.chmod(output_path, stat.S_IRUSR | stat.S_IWUSR)
 
 
-'''
-@params compressed: filename, Asset, or file-like object to uncompress
-@params uncompressed: filename to uncompress into
-@params format: optional compression format (gzip, lzma)
-
-Uncompresses @compressed into @uncompressed
-
-If @format is None, heuristics will be applied to guess the format
-from the filename or Asset URL. @format must be non-None if @uncompressed
-is a file-like object.
-
-Returns the fully qualified path to the uncompessed file
-'''
 def uncompress(compressed, uncompressed, format=None):
+    '''
+    @params compressed: filename, Asset, or file-like object to uncompress
+    @params uncompressed: filename to uncompress into
+    @params format: optional compression format (gzip, lzma)
+
+    Uncompresses @compressed into @uncompressed
+
+    If @format is None, heuristics will be applied to guess the
+    format from the filename or Asset URL. @format must be non-None
+    if @uncompressed is a file-like object.
+
+    Returns the fully qualified path to the uncompessed file
+    '''
     if format is None:
         format = guess_uncompress_format(compressed)
 
@@ -84,19 +84,19 @@ def uncompress(compressed, uncompressed, format=None):
     else:
         raise Exception(f"Unknown compression format {format}")
 
-'''
-@params compressed: filename, Asset, or file-like object to guess
-
-Guess the format of @compressed, raising an exception if
-no format can be determined
-'''
 def guess_uncompress_format(compressed):
-    if type(compressed) == Asset:
+    '''
+    @params compressed: filename, Asset, or file-like object to guess
+
+    Guess the format of @compressed, raising an exception if
+    no format can be determined
+    '''
+    if isinstance(compressed, Asset):
         compressed = urlparse(compressed.url).path
-    elif type(compressed) != str:
+    elif not isinstance(compressed, str):
         raise Exception(f"Unable to guess compression cformat for {compressed}")
 
-    (name, ext) = os.path.splitext(compressed)
+    (_name, ext) = os.path.splitext(compressed)
     if ext == ".xz":
         return "xz"
     elif ext == ".gz":
diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py
index e7c8de8..826c267 100644
--- a/tests/functional/qemu_test/utils.py
+++ b/tests/functional/qemu_test/utils.py
@@ -17,10 +17,10 @@ def get_usernet_hostfwd_port(vm):
     res = vm.cmd('human-monitor-command', command_line='info usernet')
     return get_info_usernet_hostfwd_port(res)
 
-"""
-Round up to next power of 2
-"""
 def pow2ceil(x):
+    """
+    Round up to next power of 2
+    """
     return 1 if x == 0 else 2**(x - 1).bit_length()
 
 def file_truncate(path, size):
@@ -28,12 +28,12 @@ def file_truncate(path, size):
         with open(path, 'ab+') as fd:
             fd.truncate(size)
 
-"""
-Expand file size to next power of 2
-"""
 def image_pow2ceil_expand(path):
-        size = os.path.getsize(path)
-        size_aligned = pow2ceil(size)
-        if size != size_aligned:
-            with open(path, 'ab+') as fd:
-                fd.truncate(size_aligned)
+    """
+    Expand file size to next power of 2
+    """
+    size = os.path.getsize(path)
+    size_aligned = pow2ceil(size)
+    if size != size_aligned:
+        with open(path, 'ab+') as fd:
+            fd.truncate(size_aligned)
diff --git a/tests/functional/rx/test_gdbsim.py b/tests/functional/rx/test_gdbsim.py
index 4924579..d31f9a4 100755
--- a/tests/functional/rx/test_gdbsim.py
+++ b/tests/functional/rx/test_gdbsim.py
@@ -17,9 +17,6 @@
 
 class RxGdbSimMachine(QemuSystemTest):
 
-    timeout = 30
-    KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
-
     ASSET_UBOOT = Asset(
         ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/'
          'u-boot.bin'),
@@ -47,7 +44,7 @@ def test_uboot(self):
         self.vm.launch()
         uboot_version = 'U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty'
         wait_for_console_pattern(self, uboot_version)
-        gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)'
+        #gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)'
         # FIXME limit baudrate on chardev, else we type too fast
         #  https://gitlab.com/qemu-project/qemu/-/issues/2691
         #exec_command_and_wait_for_pattern(self, 'version', gcc_version)
@@ -63,7 +60,6 @@ def test_linux_sash(self):
         kernel_path = self.ASSET_KERNEL.fetch()
 
         self.vm.set_console()
-        kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon'
         self.vm.add_args('-kernel', kernel_path,
                          '-dtb', dtb_path,
                          '-no-reboot')
diff --git a/tests/functional/x86_64/test_acpi_bits.py b/tests/functional/x86_64/test_acpi_bits.py
index 9a28165..ec716d6 100755
--- a/tests/functional/x86_64/test_acpi_bits.py
+++ b/tests/functional/x86_64/test_acpi_bits.py
@@ -57,6 +57,7 @@ class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods
     """
     def __init__(self,
                  binary: str,
+                 *,
                  args: Sequence[str] = (),
                  wrapper: Sequence[str] = (),
                  name: Optional[str] = None,
@@ -225,7 +226,7 @@ def generate_bits_iso(self):
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT,
                                       check=True)
-                self.log.info("grub-mkrescue output %s" % proc.stdout)
+                self.log.info("grub-mkrescue output %s", proc.stdout)
             else:
                 subprocess.check_call([mkrescue_script, '-o',
                                       iso_file, bits_dir],
@@ -287,9 +288,8 @@ def parse_log(self):
             except AssertionError as e:
                 self._print_log(log)
                 raise e
-            else:
-                if os.getenv('V') or os.getenv('BITS_DEBUG'):
-                    self._print_log(log)
+            if os.getenv('V') or os.getenv('BITS_DEBUG'):
+                self._print_log(log)
 
     def tearDown(self):
         """
diff --git a/tests/functional/x86_64/test_virtio_balloon.py b/tests/functional/x86_64/test_virtio_balloon.py
index 5877b6c..7a579e0 100755
--- a/tests/functional/x86_64/test_virtio_balloon.py
+++ b/tests/functional/x86_64/test_virtio_balloon.py
@@ -66,7 +66,7 @@ def assert_initial_stats(self):
         when = ret.get('last-update')
         assert when == 0
         stats = ret.get('stats')
-        for name, val in stats.items():
+        for _name, val in stats.items():
             assert val == UNSET_STATS_VALUE
 
     def assert_running_stats(self, then):
@@ -87,10 +87,10 @@ def assert_running_stats(self, then):
 
         now = time.time()
 
-        assert when > then and when < now
+        assert now > when > then
         stats = ret.get('stats')
         # Stat we expect this particular Kernel to have set
-        expectData = [
+        expect_data = [
             "stat-available-memory",
             "stat-disk-caches",
             "stat-free-memory",
@@ -103,7 +103,7 @@ def assert_running_stats(self, then):
             "stat-total-memory",
         ]
         for name, val in stats.items():
-            if name in expectData:
+            if name in expect_data:
                 assert val != UNSET_STATS_VALUE
             else:
                 assert val == UNSET_STATS_VALUE
diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target
index 8cd4667..a4425d3 100644
--- a/tests/tcg/s390x/Makefile.softmmu-target
+++ b/tests/tcg/s390x/Makefile.softmmu-target
@@ -28,6 +28,7 @@
     mc                                                                         \
     per                                                                        \
     precise-smc-softmmu                                                        \
+    sckc                                                                       \
     ssm-early                                                                  \
     stosm-early                                                                \
     stpq                                                                       \
diff --git a/tests/tcg/s390x/sckc.S b/tests/tcg/s390x/sckc.S
new file mode 100644
index 0000000..ecd64a3
--- /dev/null
+++ b/tests/tcg/s390x/sckc.S
@@ -0,0 +1,63 @@
+/*
+ * Test clock comparator.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+    .org 0x130
+ext_old_psw:
+    .org 0x1b0
+ext_new_psw:
+    .quad 0x180000000, _ext            /* 64-bit mode */
+    .org 0x1d0
+pgm_new_psw:
+    .quad 0x2000000000000,0            /* disabled wait */
+    .org 0x200                         /* lowcore padding */
+
+    .globl _start
+_start:
+    lpswe start31_psw
+_start31:
+    stctg %c0,%c0,c0
+    oi c0+6,8                          /* set clock-comparator subclass mask */
+    lctlg %c0,%c0,c0
+
+0:
+    brasl %r14,_f                      /* %r14's most significant bit is 1 */
+    jg 0b
+_f:
+    br %r14                            /* it must not end up in ext_old_psw */
+
+_ext:
+    stg %r0,ext_saved_r0
+
+    lg %r0,ext_counter
+    aghi %r0,1
+    stg %r0,ext_counter
+
+    cgfi %r0,0x1000
+    jnz 0f
+    lpswe success_psw
+0:
+
+    stck clock
+    lg %r0,clock
+    agfi %r0,0x40000                   /* 64us * 0x1000 =~ 0.25s */
+    stg %r0,clock
+    sckc clock
+
+    lg %r0,ext_saved_r0
+    lpswe ext_old_psw
+
+    .align 8
+start31_psw:
+    .quad 0x100000080000000,_start31   /* EX, 31-bit mode */
+success_psw:
+    .quad 0x2000000000000,0xfff        /* see is_special_wait_psw() */
+c0:
+    .skip 8
+clock:
+    .quad 0
+ext_counter:
+    .quad 0
+ext_saved_r0:
+    .skip 8
diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c
index 0ead6bf..c24200a 100644
--- a/tests/unit/test-aio-multithread.c
+++ b/tests/unit/test-aio-multithread.c
@@ -443,7 +443,7 @@
 
 int main(int argc, char **argv)
 {
-    init_clocks(NULL);
+    qemu_init_clocks(NULL);
 
     g_test_init(&argc, &argv, NULL);
     g_test_add_func("/aio/multi/lifecycle", test_lifecycle);
diff --git a/util/hexdump.c b/util/hexdump.c
index f29ffce..7cfc547 100644
--- a/util/hexdump.c
+++ b/util/hexdump.c
@@ -22,6 +22,19 @@
     return (x < 10 ? '0' : 'a' - 10) + x;
 }
 
+static size_t hexdump_line_length(size_t buf_len, size_t unit_len,
+                                  size_t block_len)
+{
+    size_t est = buf_len * 2;
+    if (unit_len) {
+        est += buf_len / unit_len;
+    }
+    if (block_len) {
+        est += buf_len / block_len;
+    }
+    return est;
+}
+
 GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len,
                            size_t unit_len, size_t block_len)
 {
@@ -30,14 +43,8 @@
 
     if (str == NULL) {
         /* Estimate the length of the output to avoid reallocs. */
-        size_t est = len * 2;
-        if (unit_len) {
-            est += len / unit_len;
-        }
-        if (block_len) {
-            est += len / block_len;
-        }
-        str = g_string_sized_new(est + 1);
+        str = g_string_sized_new(hexdump_line_length(len, unit_len, block_len)
+                                 + 1);
     }
 
     for (u = 0, b = 0; len; u++, b++, len--, buf++) {
@@ -76,13 +83,16 @@
 }
 
 #define QEMU_HEXDUMP_LINE_BYTES 16
-#define QEMU_HEXDUMP_LINE_WIDTH \
-    (QEMU_HEXDUMP_LINE_BYTES * 2 + QEMU_HEXDUMP_LINE_BYTES / 4)
+#define QEMU_HEXDUMP_UNIT 1
+#define QEMU_HEXDUMP_BLOCK 4
 
 void qemu_hexdump(FILE *fp, const char *prefix,
                   const void *bufptr, size_t size)
 {
-    g_autoptr(GString) str = g_string_sized_new(QEMU_HEXDUMP_LINE_WIDTH + 1);
+    int width = hexdump_line_length(QEMU_HEXDUMP_LINE_BYTES,
+                                    QEMU_HEXDUMP_UNIT,
+                                    QEMU_HEXDUMP_BLOCK);
+    g_autoptr(GString) str = g_string_sized_new(width + 1);
     char ascii[QEMU_HEXDUMP_LINE_BYTES + 1];
     size_t b, len;
 
@@ -90,11 +100,11 @@
         len = MIN(size - b, QEMU_HEXDUMP_LINE_BYTES);
 
         g_string_truncate(str, 0);
-        qemu_hexdump_line(str, bufptr + b, len, 1, 4);
+        qemu_hexdump_line(str, bufptr + b, len,
+                          QEMU_HEXDUMP_UNIT, QEMU_HEXDUMP_BLOCK);
         asciidump_line(ascii, bufptr + b, len);
 
-        fprintf(fp, "%s: %04zx: %-*s %s\n",
-                prefix, b, QEMU_HEXDUMP_LINE_WIDTH, str->str, ascii);
+        fprintf(fp, "%s: %04zx: %-*s %s\n", prefix, b, width, str->str, ascii);
     }
 
 }
diff --git a/util/main-loop.c b/util/main-loop.c
index b8ddda8..b462598 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -162,7 +162,7 @@
     int ret;
     GSource *src;
 
-    init_clocks(qemu_timer_notify_cb);
+    qemu_init_clocks(qemu_timer_notify_cb);
 
     ret = qemu_signal_init(errp);
     if (ret) {
diff --git a/util/qemu-thread-common.h b/util/qemu-thread-common.h
index 2af6b12..0933184 100644
--- a/util/qemu-thread-common.h
+++ b/util/qemu-thread-common.h
@@ -14,6 +14,7 @@
 #define QEMU_THREAD_COMMON_H
 
 #include "qemu/thread.h"
+#include "qemu/main-loop.h"
 #include "trace.h"
 
 static inline void qemu_mutex_post_init(QemuMutex *mutex)
@@ -39,6 +40,9 @@
     mutex->line = line;
 #endif
     trace_qemu_mutex_locked(mutex, file, line);
+    if (mutex_is_bql(mutex)) {
+        bql_update_status(true);
+    }
 }
 
 static inline void qemu_mutex_pre_unlock(QemuMutex *mutex,
@@ -49,6 +53,9 @@
     mutex->line = 0;
 #endif
     trace_qemu_mutex_unlock(mutex, file, line);
+    if (mutex_is_bql(mutex)) {
+        bql_update_status(false);
+    }
 }
 
 #endif
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 56f11b6..2a6be4c 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -637,7 +637,7 @@
     return cpus_set_virtual_clock(time);
 }
 
-void init_clocks(QEMUTimerListNotifyCB *notify_cb)
+void qemu_init_clocks(QEMUTimerListNotifyCB *notify_cb)
 {
     QEMUClockType type;
     for (type = 0; type < QEMU_CLOCK_MAX; type++) {