From 7ed514fbe404a07d773ad7b335b83251839595e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 22 Aug 2025 13:42:41 +0200 Subject: [PATCH 1/7] cpus: Access CPUState::thread_kicked atomically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpus_kick_thread() is called via cpu_exit() -> qemu_cpu_kick(), and also via gdb_syscall_handling(). Access the CPUState field using atomic accesses. See commit 8ac2ca02744 ("accel: use atomic accesses for exit_request") for rationale. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Message-Id: <20250925025520.71805-3-philmd@linaro.org> --- system/cpus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/cpus.c b/system/cpus.c index aa7bfcf56e..74f5a5bd4e 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -480,10 +480,10 @@ void qemu_process_cpu_events(CPUState *cpu) 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); From fc2380b5686e8233f406a3e43be278fd9a2603e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 26 Aug 2025 06:54:12 +0200 Subject: [PATCH 2/7] accel/tcg: Use cpu_is_stopped() helper to access CPUState::stopped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250925025520.71805-5-philmd@linaro.org> --- accel/tcg/tcg-accel-ops-rr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 2fb4643997..f84342e044 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -197,7 +197,7 @@ static void *rr_cpu_thread_fn(void *arg) 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 */ From c89d1c879ab0281d481e460644eb5d56ace1a038 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 4 Sep 2025 18:31:58 -0400 Subject: [PATCH 3/7] bql: Fix bql_locked status with condvar APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU has a per-thread "bql_locked" variable stored in TLS section, showing whether the current thread is holding the BQL lock. It's a pretty handy variable. Function-wise, QEMU have codes trying to conditionally take bql, relying on the var reflecting the locking status (e.g. BQL_LOCK_GUARD), or in a GDB debugging session, we could also look at the variable (in reality, co_tls_bql_locked), to see which thread is currently holding the bql. When using that as a debugging facility, sometimes we can observe multiple threads holding bql at the same time. It's because QEMU's condvar APIs bypassed the bql_*() API, hence they do not update bql_locked even if they have released the mutex while waiting. It can cause confusion if one does "thread apply all p co_tls_bql_locked" and see multiple threads reporting true. Fix this by moving the bql status updates into the mutex debug hooks. Now the variable should always reflect the reality. Signed-off-by: Peter Xu Reviewed-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250904223158.1276992-1-peterx@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- include/qemu/main-loop.h | 18 ++++++++++++++++++ stubs/iothread-lock.c | 9 +++++++++ system/cpus.c | 14 ++++++++++++-- util/qemu-thread-common.h | 7 +++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 4e2436b196..0d55c636b2 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -270,6 +270,24 @@ void rust_bql_mock_lock(void); */ 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 * diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index 6050c081f5..c89c9c7228 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -34,3 +34,12 @@ void bql_block_unlock(bool increase) 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/system/cpus.c b/system/cpus.c index 74f5a5bd4e..ef2d2f241f 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -524,6 +524,18 @@ bool qemu_in_vcpu_thread(void) 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 @@ void bql_lock_impl(const char *file, int line) 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/util/qemu-thread-common.h b/util/qemu-thread-common.h index 2af6b12085..09331843ba 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 @@ static inline void qemu_mutex_post_lock(QemuMutex *mutex, 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 @@ static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, mutex->line = 0; #endif trace_qemu_mutex_unlock(mutex, file, line); + if (mutex_is_bql(mutex)) { + bql_update_status(false); + } } #endif From e9048f099b3142fb21ed40cd3ce0557d44e56d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 1 Oct 2025 16:10:21 +0200 Subject: [PATCH 4/7] exec/cpu: Declare cpu_memory_rw_debug() in 'hw/core/cpu.h' and document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_memory_rw_debug() dispatches to CPUClass::memory_rw_debug(), move its declaration closer to the CPU API. Document. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20251001150529.14122-22-philmd@linaro.org> --- include/exec/cpu-common.h | 4 ---- include/hw/core/cpu.h | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 67e15c8e50..e0be4ee2b8 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -150,10 +150,6 @@ typedef int (RAMBlockIterFunc)(RAMBlock *rb, void *opaque); 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 e79e8e0a8e..9615051774 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -688,6 +688,26 @@ int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, 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 From 91634cc3311d28f39ad4b82a8b92c9aeae6e4c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 30 Oct 2025 17:33:02 +0000 Subject: [PATCH 5/7] timers: properly prefix init_clocks() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we run the risk of name clashing, for example with stm32l4x5_usart-test.c should we shuffle the includes. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20251030173302.1379174-1-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/qemu/timer.h | 5 +++-- tests/unit/test-aio-multithread.c | 2 +- util/main-loop.c | 2 +- util/qemu-timer.c | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 406d741120..8b561cd696 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -786,11 +786,12 @@ static inline int64_t qemu_soonest_timeout(int64_t timeout1, int64_t timeout2) } /** - * 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/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c index 0ead6bf34a..c24200a712 100644 --- a/tests/unit/test-aio-multithread.c +++ b/tests/unit/test-aio-multithread.c @@ -443,7 +443,7 @@ static void test_multi_mutex_10(void) 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/main-loop.c b/util/main-loop.c index b8ddda8f5e..b462598f76 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -162,7 +162,7 @@ int qemu_init_main_loop(Error **errp) 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-timer.c b/util/qemu-timer.c index 56f11b6a64..2a6be4c7f9 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -637,7 +637,7 @@ static void qemu_virtual_clock_set_ns(int64_t time) 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++) { From 20aa05edc2ca547590dfa09909d5745203f57b43 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 31 Oct 2025 22:02:45 +0300 Subject: [PATCH 6/7] util/hexdump: fix QEMU_HEXDUMP_LINE_WIDTH logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU_HEXDUMP_LINE_WIDTH calculation doesn't correspond to qemu_hexdump_line(). This leads to last line of the dump (when length is not multiply of 16) has badly aligned ASCII part. Let's calculate length the same way. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20251031190246.257153-2-vsementsov@yandex-team.ru> Signed-off-by: Philippe Mathieu-Daudé --- util/hexdump.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/util/hexdump.c b/util/hexdump.c index f29ffceb74..7cfc547261 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -22,6 +22,19 @@ static inline char hexdump_nibble(unsigned x) 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 @@ GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len, 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 @@ static void asciidump_line(char *line, const void *bufptr, size_t len) } #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 @@ void qemu_hexdump(FILE *fp, const char *prefix, 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); } } From 6c5571e72aed54194a1a07799d8c23c90b5f229e Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 30 Oct 2025 17:59:32 +0100 Subject: [PATCH 7/7] rx: cpu: fix interrupts check in rx_cpu_do_interrupt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 87511341c30 broke interrupt handling, replacing interrupts fetch with a bool and then the remaining code attempting to check individual bits on that bool value, which effectively masked those interrupts. Fix it by checking individual interrupt bits directly instead of old 'fetch then check' approach. Fixes: 87511341c30d ("add cpu_test_interrupt()/cpu_set_interrupt() helpers and use them tree wide") Reported-by: Thomas Huth Signed-off-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Tested-by: Thomas Huth Reviewed-by: Thomas Huth Message-ID: <20251030165932.138512-1-imammedo@redhat.com> [PMD: Rebased on commit dde21df2393 "call plugin trap callbacks"] Signed-off-by: Philippe Mathieu-Daudé --- target/rx/helper.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/target/rx/helper.c b/target/rx/helper.c index ef47e32add..e9a7aaf610 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -41,11 +41,9 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) 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 @@ void rx_cpu_do_interrupt(CPUState *cs) 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";